diff --git a/aws-redshiftserverless-endpointaccess/.gitignore b/aws-redshiftserverless-endpointaccess/.gitignore
new file mode 100644
index 0000000..faa9259
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/.gitignore
@@ -0,0 +1,23 @@
+# macOS
+.DS_Store
+._*
+
+# Maven outputs
+.classpath
+
+# IntelliJ
+*.iml
+.idea
+out.java
+out/
+.settings
+.project
+
+# auto-generated files
+target/
+
+# our logs
+rpdk.log*
+
+# contains credentials
+sam-tests/
diff --git a/aws-redshiftserverless-endpointaccess/.rpdk-config b/aws-redshiftserverless-endpointaccess/.rpdk-config
new file mode 100644
index 0000000..3ac602a
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/.rpdk-config
@@ -0,0 +1,28 @@
+{
+ "artifact_type": "RESOURCE",
+ "typeName": "AWS::RedshiftServerless::EndpointAccess",
+ "language": "java",
+ "runtime": "java8",
+ "entrypoint": "software.amazon.redshiftserverless.endpointaccess.HandlerWrapper::handleRequest",
+ "testEntrypoint": "software.amazon.redshiftserverless.endpointaccess.HandlerWrapper::testEntrypoint",
+ "settings": {
+ "version": false,
+ "subparser_name": null,
+ "verbose": 0,
+ "force": false,
+ "type_name": null,
+ "artifact_type": null,
+ "endpoint_url": null,
+ "region": null,
+ "target_schemas": [],
+ "namespace": [
+ "software",
+ "amazon",
+ "redshiftserverless",
+ "endpointaccess"
+ ],
+ "codegen_template_path": "guided_aws",
+ "protocolVersion": "2.0.0"
+ },
+ "executableEntrypoint": "software.amazon.redshiftserverless.endpointaccess.HandlerWrapperExecutable"
+}
diff --git a/aws-redshiftserverless-endpointaccess/README.md b/aws-redshiftserverless-endpointaccess/README.md
new file mode 100644
index 0000000..df260a2
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/README.md
@@ -0,0 +1,12 @@
+# AWS::RedshiftServerless::EndpointAccess
+
+Congratulations on starting development! Next steps:
+
+1. Write the JSON schema describing your resource, `aws-redshiftserverless-endpointaccess.json`
+1. Implement your resource handlers.
+
+The RPDK will automatically generate the correct resource model from the schema whenever the project is built via Maven. You can also do this manually with the following command: `cfn generate`.
+
+> Please don't modify files under `target/generated-sources/rpdk`, as they will be automatically overwritten.
+
+The code uses [Lombok](https://projectlombok.org/), and [you may have to install IDE integrations](https://projectlombok.org/setup/overview) to enable auto-complete for Lombok-annotated classes.
diff --git a/aws-redshiftserverless-endpointaccess/aws-redshiftserverless-endpointaccess.json b/aws-redshiftserverless-endpointaccess/aws-redshiftserverless-endpointaccess.json
new file mode 100644
index 0000000..f126f7c
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/aws-redshiftserverless-endpointaccess.json
@@ -0,0 +1,221 @@
+{
+ "typeName": "AWS::RedshiftServerless::EndpointAccess",
+ "description": "Resource schema for a Redshift Serverless managed VPC endpoint.",
+ "sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-redshift-serverless",
+ "definitions": {
+ "VpcSecurityGroupMembership": {
+ "description": "Describes the members of a VPC security group associated with the workgroup.",
+ "type": "object",
+ "properties": {
+ "VpcSecurityGroupId": {
+ "type": "string",
+ "description": "The identifier of the VPC security group."
+ },
+ "Status": {
+ "type": "string",
+ "description": "The status of the VPC security group."
+ }
+ },
+ "additionalProperties": false
+ },
+ "NetworkInterface": {
+ "description": "Describes a network interface.",
+ "type": "object",
+ "properties": {
+ "NetworkInterfaceId": {
+ "type": "string",
+ "description": "The network interface identifier."
+ },
+ "SubnetId": {
+ "type": "string",
+ "description": "The subnet identifier."
+ },
+ "PrivateIpAddress": {
+ "type": "string",
+ "description": "The IPv4 address of the network interface within the subnet."
+ },
+ "AvailabilityZone": {
+ "type": "string",
+ "description": "The Availability Zone."
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "properties": {
+ "EndpointName": {
+ "description": "The name of the endpoint.",
+ "type": "string",
+ "pattern": "^(?=^[a-z][a-z0-9]*(-[a-z0-9]+)*$).{1,30}$"
+ },
+ "EndpointStatus": {
+ "description": "The status of the VPC endpoint.",
+ "type": "string"
+ },
+ "WorkgroupName": {
+ "description": "The name of the workgroup.",
+ "type": "string",
+ "pattern": "^(?=^[a-z0-9-]+$).{3,64}$",
+ "maxLength": 64,
+ "minLength": 3
+ },
+ "EndpointCreateTime": {
+ "description": "The time (UTC) that the endpoint was created.",
+ "type": "string"
+ },
+ "Port": {
+ "description": "The port number on which Amazon Redshift Serverless accepts incoming connections.",
+ "type": "integer"
+ },
+ "Address": {
+ "description": "The DNS address of the endpoint.",
+ "type": "string"
+ },
+ "SubnetIds": {
+ "description": "The unique identifier of subnets where Amazon Redshift Serverless choose to deploy the VPC endpoint.",
+ "type": "array",
+ "items": {
+ "type": "string",
+ "pattern": "^sg-[0-9a-fA-F]{8,}$",
+ "maxLength": 255,
+ "minLength": 0
+ },
+ "insertionOrder": false
+ },
+ "VpcSecurityGroups": {
+ "description": "A list of Virtual Private Cloud (VPC) security groups to be associated with the endpoint.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/VpcSecurityGroupMembership"
+ },
+ "insertionOrder": false
+ },
+ "VpcEndpoint": {
+ "description": "The connection endpoint for connecting to Amazon Redshift Serverless.",
+ "type": "object",
+ "properties": {
+ "VpcEndpointId": {
+ "type": "string",
+ "description": "The connection endpoint ID for connecting to Amazon Redshift Serverless."
+ },
+ "VpcId": {
+ "type": "string",
+ "description": "The VPC identifier that the endpoint is associated with."
+ },
+ "NetworkInterfaces": {
+ "type": "array",
+ "description": "One or more network interfaces of the endpoint. Also known as an interface endpoint.",
+ "items": {
+ "$ref": "#/definitions/NetworkInterface"
+ },
+ "insertionOrder": false
+ }
+ },
+ "additionalProperties": false
+ },
+ "EndpointArn": {
+ "description": "The Amazon Resource Name (ARN) of the VPC endpoint.",
+ "type": "string"
+ },
+ "OwnerAccount": {
+ "description": "Account Id of the resource owner",
+ "type": "string"
+ },
+ "VpcSecurityGroupIds": {
+ "description": "A list of VPC security group IDs to associate with the workgroup.",
+ "type": "array",
+ "insertionOrder": false,
+ "items": {
+ "type": "string",
+ "pattern": "^sg-[0-9a-fA-F]{8,}$",
+ "maxLength": 255,
+ "minLength": 0
+ }
+ },
+ "VpcId": {
+ "type": "string"
+ }
+ },
+ "tagging": {
+ "taggable": false
+ },
+ "additionalProperties": false,
+ "required": [
+ "EndpointName"
+ ],
+ "primaryIdentifier": [
+ "/properties/EndpointName"
+ ],
+ "createOnlyProperties": [
+ "/properties/EndpointName"
+ ],
+ "readOnlyProperties": [
+ "/properties/Address",
+ "/properties/EndpointStatus",
+ "/properties/EndpointCreateTime",
+ "/properties/Port",
+ "/properties/VpcSecurityGroups",
+ "/properties/VpcEndpoint",
+ "/properties/EndpointArn"
+ ],
+ "handlers": {
+ "create": {
+ "permissions": [
+ "redshift-serverless:CreateEndpointAccess",
+ "ec2:CreateClientVpnEndpoint",
+ "ec2:DescribeVpcAttribute",
+ "ec2:DescribeSecurityGroups",
+ "ec2:DescribeAddresses",
+ "ec2:DescribeInternetGateways",
+ "ec2:DescribeSubnets"
+ ],
+ "timeoutInMinutes": 60
+ },
+ "read": {
+ "permissions": [
+ "redshift-serverless:GetEndpointAccess",
+ "ec2:DescribeClientVpnEndpoints",
+ "ec2:DescribeVpcAttribute",
+ "ec2:DescribeSecurityGroups",
+ "ec2:DescribeAddresses",
+ "ec2:DescribeInternetGateways",
+ "ec2:DescribeSubnets"
+ ]
+ },
+ "update": {
+ "permissions": [
+ "redshift-serverless:UpdateEndpointAccess",
+ "ec2:ModifyClientVpnEndpoint",
+ "ec2:DescribeVpcAttribute",
+ "ec2:DescribeSecurityGroups",
+ "ec2:DescribeAddresses",
+ "ec2:DescribeInternetGateways",
+ "ec2:DescribeSubnets"
+ ],
+ "timeoutInMinutes": 60
+ },
+ "delete": {
+ "permissions": [
+ "redshift-serverless:DeleteEndpointAccess",
+ "ec2:DeleteClientVpnEndpoint",
+ "ec2:DescribeVpcAttribute",
+ "ec2:DescribeSecurityGroups",
+ "ec2:DescribeAddresses",
+ "ec2:DescribeInternetGateways",
+ "ec2:DescribeSubnets"
+ ],
+ "timeoutInMinutes": 60
+ },
+ "list": {
+ "permissions": [
+ "redshift-serverless:ListEndpointAccess",
+ "ec2:DescribeClientVpnEndpoints",
+ "ec2:DescribeVpcAttribute",
+ "ec2:DescribeSecurityGroups",
+ "ec2:DescribeAddresses",
+ "ec2:DescribeInternetGateways",
+ "ec2:DescribeSubnets"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/aws-redshiftserverless-endpointaccess/docs/README.md b/aws-redshiftserverless-endpointaccess/docs/README.md
new file mode 100644
index 0000000..4746262
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/docs/README.md
@@ -0,0 +1,147 @@
+# AWS::RedshiftServerless::EndpointAccess
+
+Resource schema for a Redshift Serverless managed VPC endpoint.
+
+## Syntax
+
+To declare this entity in your AWS CloudFormation template, use the following syntax:
+
+### JSON
+
+
+{
+ "Type" : "AWS::RedshiftServerless::EndpointAccess",
+ "Properties" : {
+ "EndpointName " : String ,
+ "WorkgroupName " : String ,
+ "SubnetIds " : [ String, ... ] ,
+ "OwnerAccount " : String ,
+ "VpcSecurityGroupIds " : [ String, ... ] ,
+ "VpcId " : String
+ }
+}
+
+
+### YAML
+
+
+Type: AWS::RedshiftServerless::EndpointAccess
+Properties:
+ EndpointName : String
+ WorkgroupName : String
+ SubnetIds :
+ - String
+ OwnerAccount : String
+ VpcSecurityGroupIds :
+ - String
+ VpcId : String
+
+
+## Properties
+
+#### EndpointName
+
+The name of the endpoint.
+
+_Required_: Yes
+
+_Type_: String
+
+_Pattern_: ^(?=^[a-z][a-z0-9]*(-[a-z0-9]+)*$).{1,30}$
+
+_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)
+
+#### WorkgroupName
+
+The name of the workgroup.
+
+_Required_: No
+
+_Type_: String
+
+_Minimum Length_: 3
+
+_Maximum Length_: 64
+
+_Pattern_: ^(?=^[a-z0-9-]+$).{3,64}$
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### SubnetIds
+
+The unique identifier of subnets where Amazon Redshift Serverless choose to deploy the VPC endpoint.
+
+_Required_: No
+
+_Type_: List of String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### OwnerAccount
+
+Account Id of the resource owner
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### VpcSecurityGroupIds
+
+A list of VPC security group IDs to associate with the workgroup.
+
+_Required_: No
+
+_Type_: List of String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### VpcId
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+## Return Values
+
+### Ref
+
+When you pass the logical ID of this resource to the intrinsic `Ref` function, Ref returns the EndpointName.
+
+### Fn::GetAtt
+
+The `Fn::GetAtt` intrinsic function returns a value for a specified attribute of this type. The following are the available attributes and sample return values.
+
+For more information about using the `Fn::GetAtt` intrinsic function, see [Fn::GetAtt](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html).
+
+#### Address
+
+The DNS address of the endpoint.
+
+#### EndpointStatus
+
+The status of the VPC endpoint.
+
+#### EndpointCreateTime
+
+The time (UTC) that the endpoint was created.
+
+#### Port
+
+The port number on which Amazon Redshift Serverless accepts incoming connections.
+
+#### VpcSecurityGroups
+
+A list of Virtual Private Cloud (VPC) security groups to be associated with the endpoint.
+
+#### VpcEndpoint
+
+The connection endpoint for connecting to Amazon Redshift Serverless.
+
+#### EndpointArn
+
+The Amazon Resource Name (ARN) of the VPC endpoint.
+
diff --git a/aws-redshiftserverless-endpointaccess/docs/networkinterface.md b/aws-redshiftserverless-endpointaccess/docs/networkinterface.md
new file mode 100644
index 0000000..324d6e7
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/docs/networkinterface.md
@@ -0,0 +1,70 @@
+# AWS::RedshiftServerless::EndpointAccess NetworkInterface
+
+Describes a network interface.
+
+## Syntax
+
+To declare this entity in your AWS CloudFormation template, use the following syntax:
+
+### JSON
+
+
+{
+ "NetworkInterfaceId " : String ,
+ "SubnetId " : String ,
+ "PrivateIpAddress " : String ,
+ "AvailabilityZone " : String
+}
+
+
+### YAML
+
+
+NetworkInterfaceId : String
+SubnetId : String
+PrivateIpAddress : String
+AvailabilityZone : String
+
+
+## Properties
+
+#### NetworkInterfaceId
+
+The network interface identifier.
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### SubnetId
+
+The subnet identifier.
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### PrivateIpAddress
+
+The IPv4 address of the network interface within the subnet.
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### AvailabilityZone
+
+The Availability Zone.
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
diff --git a/aws-redshiftserverless-endpointaccess/docs/vpcendpoint.md b/aws-redshiftserverless-endpointaccess/docs/vpcendpoint.md
new file mode 100644
index 0000000..3077eac
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/docs/vpcendpoint.md
@@ -0,0 +1,59 @@
+# AWS::RedshiftServerless::EndpointAccess VpcEndpoint
+
+The connection endpoint for connecting to Amazon Redshift Serverless.
+
+## Syntax
+
+To declare this entity in your AWS CloudFormation template, use the following syntax:
+
+### JSON
+
+
+{
+ "VpcEndpointId " : String ,
+ "VpcId " : String ,
+ "NetworkInterfaces " : [ NetworkInterface , ... ]
+}
+
+
+### YAML
+
+
+VpcEndpointId : String
+VpcId : String
+NetworkInterfaces :
+ - NetworkInterface
+
+
+## Properties
+
+#### VpcEndpointId
+
+The connection endpoint ID for connecting to Amazon Redshift Serverless.
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### VpcId
+
+The VPC identifier that the endpoint is associated with.
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### NetworkInterfaces
+
+One or more network interfaces of the endpoint. Also known as an interface endpoint.
+
+_Required_: No
+
+_Type_: List of NetworkInterface
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
diff --git a/aws-redshiftserverless-endpointaccess/docs/vpcsecuritygroupmembership.md b/aws-redshiftserverless-endpointaccess/docs/vpcsecuritygroupmembership.md
new file mode 100644
index 0000000..642eb95
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/docs/vpcsecuritygroupmembership.md
@@ -0,0 +1,46 @@
+# AWS::RedshiftServerless::EndpointAccess VpcSecurityGroupMembership
+
+Describes the members of a VPC security group associated with the workgroup.
+
+## Syntax
+
+To declare this entity in your AWS CloudFormation template, use the following syntax:
+
+### JSON
+
+
+{
+ "VpcSecurityGroupId " : String ,
+ "Status " : String
+}
+
+
+### YAML
+
+
+VpcSecurityGroupId : String
+Status : String
+
+
+## Properties
+
+#### VpcSecurityGroupId
+
+The identifier of the VPC security group.
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### Status
+
+The status of the VPC security group.
+
+_Required_: No
+
+_Type_: String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
diff --git a/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_create.json b/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_create.json
new file mode 100644
index 0000000..46ed7da
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_create.json
@@ -0,0 +1,12 @@
+{
+ "TPSCode": "...",
+ "Title": "...",
+ "CoverSheetIncluded": "...",
+ "DueDate": "...",
+ "ApprovalDate": "...",
+ "Memo": "...",
+ "SecondCopyOfMemo": "...",
+ "TestCode": "...",
+ "Authors": "...",
+ "Tags": "..."
+}
diff --git a/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_invalid.json b/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_invalid.json
new file mode 100644
index 0000000..46ed7da
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_invalid.json
@@ -0,0 +1,12 @@
+{
+ "TPSCode": "...",
+ "Title": "...",
+ "CoverSheetIncluded": "...",
+ "DueDate": "...",
+ "ApprovalDate": "...",
+ "Memo": "...",
+ "SecondCopyOfMemo": "...",
+ "TestCode": "...",
+ "Authors": "...",
+ "Tags": "..."
+}
diff --git a/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_update.json b/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_update.json
new file mode 100644
index 0000000..46ed7da
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/example_inputs/inputs_1_update.json
@@ -0,0 +1,12 @@
+{
+ "TPSCode": "...",
+ "Title": "...",
+ "CoverSheetIncluded": "...",
+ "DueDate": "...",
+ "ApprovalDate": "...",
+ "Memo": "...",
+ "SecondCopyOfMemo": "...",
+ "TestCode": "...",
+ "Authors": "...",
+ "Tags": "..."
+}
diff --git a/aws-redshiftserverless-endpointaccess/lombok.config b/aws-redshiftserverless-endpointaccess/lombok.config
new file mode 100644
index 0000000..7a21e88
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/lombok.config
@@ -0,0 +1 @@
+lombok.addLombokGeneratedAnnotation = true
diff --git a/aws-redshiftserverless-endpointaccess/overrides.json b/aws-redshiftserverless-endpointaccess/overrides.json
new file mode 100644
index 0000000..9cc4668
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/overrides.json
@@ -0,0 +1,11 @@
+{
+ "CREATE": {
+ "/OwnerAccount": null,
+ "/VpcSecurityGroupIds": null,
+ "/SubnetIds": null,
+ "/WorkgroupName": null
+ },
+ "UPDATE": {
+ "/VpcSecurityGroupIds": null
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/pom.xml b/aws-redshiftserverless-endpointaccess/pom.xml
new file mode 100644
index 0000000..01e42c6
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/pom.xml
@@ -0,0 +1,261 @@
+
+
+ 4.0.0
+
+ software.amazon.redshiftserverless.endpointaccess
+ aws-redshiftserverless-endpointaccess-handler
+ aws-redshiftserverless-endpointaccess-handler
+ 1.0-SNAPSHOT
+ jar
+
+
+ 1.8
+ 1.8
+ UTF-8
+ UTF-8
+
+
+
+
+
+
+ software.amazon.awssdk
+ redshiftserverless
+ 2.23.21
+
+
+
+ software.amazon.cloudformation
+ aws-cloudformation-rpdk-java-plugin
+ [2.0.0,3.0.0)
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.22
+ provided
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.17.1
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.17.1
+
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+ 2.13.3
+
+
+
+
+ org.assertj
+ assertj-core
+ 3.12.2
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.5.0-M1
+ test
+
+
+
+ org.mockito
+ mockito-core
+ 3.6.0
+ test
+
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 3.6.0
+ test
+
+
+
+ com.google.code.gson
+ gson
+ 2.9.0
+
+
+
+
+
+
+ software.amazon.awssdk
+ bom
+ 2.22.5
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ -Xlint:all,-options,-processing
+ -Werror
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.3
+
+ false
+
+
+ *:*
+
+ **/Log4j2Plugins.dat
+
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.6.0
+
+
+ generate
+ generate-sources
+
+ exec
+
+
+ cfn
+ generate ${cfn.generate.args}
+ ${project.basedir}
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.0.0
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+ ${project.basedir}/target/generated-sources/rpdk
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 2.4
+
+
+ maven-surefire-plugin
+ 3.0.0-M3
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.4
+
+
+ **/BaseConfiguration*
+ **/BaseHandler*
+ **/HandlerWrapper*
+ **/ResourceModel*
+
+
+
+
+
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
+
+
+
+ jacoco-check
+
+ check
+
+
+
+
+ PACKAGE
+
+
+ BRANCH
+ COVEREDRATIO
+ 0.8
+
+
+ INSTRUCTION
+ COVEREDRATIO
+ 0.8
+
+
+
+
+
+
+
+
+
+
+
+ ${project.basedir}
+
+ aws-redshiftserverless-endpointaccess.json
+
+
+
+ ${project.basedir}/target/loaded-target-schemas
+
+ **/*.json
+
+
+
+
+
diff --git a/aws-redshiftserverless-endpointaccess/resource-role.yaml b/aws-redshiftserverless-endpointaccess/resource-role.yaml
new file mode 100644
index 0000000..32b9580
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/resource-role.yaml
@@ -0,0 +1,51 @@
+AWSTemplateFormatVersion: "2010-09-09"
+Description: >
+ This CloudFormation template creates a role assumed by CloudFormation
+ during CRUDL operations to mutate resources on behalf of the customer.
+
+Resources:
+ ExecutionRole:
+ Type: AWS::IAM::Role
+ Properties:
+ MaxSessionDuration: 8400
+ AssumeRolePolicyDocument:
+ Version: '2012-10-17'
+ Statement:
+ - Effect: Allow
+ Principal:
+ Service: resources.cloudformation.amazonaws.com
+ Action: sts:AssumeRole
+ Condition:
+ StringEquals:
+ aws:SourceAccount:
+ Ref: AWS::AccountId
+ StringLike:
+ aws:SourceArn:
+ Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:type/resource/AWS-RedshiftServerless-EndpointAccess/*
+ Path: "/"
+ Policies:
+ - PolicyName: ResourceTypePolicy
+ PolicyDocument:
+ Version: '2012-10-17'
+ Statement:
+ - Effect: Allow
+ Action:
+ - "ec2:CreateClientVpnEndpoint"
+ - "ec2:DeleteClientVpnEndpoint"
+ - "ec2:DescribeAddresses"
+ - "ec2:DescribeClientVpnEndpoints"
+ - "ec2:DescribeInternetGateways"
+ - "ec2:DescribeSecurityGroups"
+ - "ec2:DescribeSubnets"
+ - "ec2:DescribeVpcAttribute"
+ - "ec2:ModifyClientVpnEndpoint"
+ - "redshift-serverless:CreateEndpointAccess"
+ - "redshift-serverless:DeleteEndpointAccess"
+ - "redshift-serverless:GetEndpointAccess"
+ - "redshift-serverless:ListEndpointAccess"
+ - "redshift-serverless:UpdateEndpointAccess"
+ Resource: "*"
+Outputs:
+ ExecutionRoleArn:
+ Value:
+ Fn::GetAtt: ExecutionRole.Arn
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/BaseHandlerStd.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/BaseHandlerStd.java
new file mode 100644
index 0000000..5eef6aa
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/BaseHandlerStd.java
@@ -0,0 +1,41 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.awssdk.services.redshiftserverless.model.RedshiftServerlessResponse;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.Logger;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+
+public abstract class BaseHandlerStd extends BaseHandler {
+ @Override
+ public final ProgressEvent handleRequest(
+ final AmazonWebServicesClientProxy proxy,
+ final ResourceHandlerRequest request,
+ final CallbackContext callbackContext,
+ final Logger logger) {
+ return handleRequest(
+ proxy,
+ request,
+ callbackContext != null ? callbackContext : new CallbackContext(),
+ proxy.newProxy(ClientBuilder::getClient),
+ logger
+ );
+ }
+
+ protected abstract ProgressEvent handleRequest(
+ final AmazonWebServicesClientProxy proxy,
+ final ResourceHandlerRequest request,
+ final CallbackContext callbackContext,
+ final ProxyClient proxyClient,
+ final Logger logger);
+
+ public boolean isEndpointAccessRequest(final Object awsRequest,
+ final RedshiftServerlessResponse awsResponse,
+ final ProxyClient proxyClient,
+ final ResourceModel model,
+ final CallbackContext context) {
+ return true;
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/CallbackContext.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/CallbackContext.java
new file mode 100644
index 0000000..2019483
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/CallbackContext.java
@@ -0,0 +1,10 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.cloudformation.proxy.StdCallbackContext;
+
+@lombok.Getter
+@lombok.Setter
+@lombok.ToString
+@lombok.EqualsAndHashCode(callSuper = true)
+public class CallbackContext extends StdCallbackContext {
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ClientBuilder.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ClientBuilder.java
new file mode 100644
index 0000000..3f47b34
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ClientBuilder.java
@@ -0,0 +1,13 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.cloudformation.LambdaWrapper;
+
+public class ClientBuilder {
+
+ public static RedshiftServerlessClient getClient() {
+ return RedshiftServerlessClient.builder()
+ .httpClient(LambdaWrapper.HTTP_CLIENT)
+ .build();
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/Configuration.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/Configuration.java
new file mode 100644
index 0000000..716232a
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/Configuration.java
@@ -0,0 +1,8 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+class Configuration extends BaseConfiguration {
+
+ public Configuration() {
+ super("aws-redshiftserverless-endpointaccess.json");
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/CreateHandler.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/CreateHandler.java
new file mode 100644
index 0000000..e5b172f
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/CreateHandler.java
@@ -0,0 +1,87 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.awssdk.awscore.AwsResponse;
+import software.amazon.awssdk.awscore.exception.AwsServiceException;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.awssdk.services.redshiftserverless.model.AccessDeniedException;
+import software.amazon.awssdk.services.redshiftserverless.model.ConflictException;
+import software.amazon.awssdk.services.redshiftserverless.model.CreateEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.CreateEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.InsufficientCapacityException;
+import software.amazon.awssdk.services.redshiftserverless.model.InternalServerException;
+import software.amazon.awssdk.services.redshiftserverless.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.redshiftserverless.model.ServiceQuotaExceededException;
+import software.amazon.awssdk.services.redshiftserverless.model.TooManyTagsException;
+import software.amazon.awssdk.services.redshiftserverless.model.ValidationException;
+import software.amazon.cloudformation.exceptions.CfnGeneralServiceException;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.HandlerErrorCode;
+import software.amazon.cloudformation.proxy.Logger;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+
+import java.util.regex.Pattern;
+
+
+public class CreateHandler extends BaseHandlerStd {
+ private Logger logger;
+
+ protected ProgressEvent handleRequest(
+ final AmazonWebServicesClientProxy proxy,
+ final ResourceHandlerRequest request,
+ final CallbackContext callbackContext,
+ final ProxyClient proxyClient,
+ final Logger logger) {
+
+ this.logger = logger;
+ final ResourceModel resourceModel = request.getDesiredResourceState();
+
+ return ProgressEvent.progress(resourceModel, callbackContext)
+ .then(progress ->
+ proxy.initiate("AWS-RedshiftServerless-EndpointAccess::Create", proxyClient, progress.getResourceModel(), progress.getCallbackContext())
+ .translateToServiceRequest(Translator::translateToCreateEndpointAccessRequest)
+ .makeServiceCall(this::createEndpointAccess)
+ .stabilize(this::isEndpointAccessRequest)
+ .handleError(this::createEndpointAccessErrorHandler)
+ .done(awsResponse -> ProgressEvent.defaultSuccessHandler(Translator.translateFromEndpointAccessResponse(awsResponse.endpoint()))));
+ }
+
+ private CreateEndpointAccessResponse createEndpointAccess(final CreateEndpointAccessRequest awsRequest,
+ final ProxyClient proxyClient) {
+ CreateEndpointAccessResponse awsResponse;
+ awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest, proxyClient.client()::createEndpointAccess);
+
+ logger.log(String.format("%s successfully created.", ResourceModel.TYPE_NAME));
+ return awsResponse;
+ }
+
+ private ProgressEvent createEndpointAccessErrorHandler(final CreateEndpointAccessRequest awsRequest,
+ final Exception exception,
+ final ProxyClient client,
+ final ResourceModel model,
+ final CallbackContext context) {
+ if (exception instanceof ValidationException ||
+ exception instanceof AccessDeniedException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.InvalidRequest);
+
+ } else if (exception instanceof ResourceNotFoundException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.NotFound);
+
+ } else if (exception instanceof InternalServerException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.InternalFailure);
+
+ } else if (exception instanceof ConflictException ||
+ exception instanceof ServiceQuotaExceededException) {
+ Pattern pattern = Pattern.compile(".*already exists.*", Pattern.CASE_INSENSITIVE);
+ HandlerErrorCode handlerErrorCode = pattern.matcher(exception.getMessage()).matches() ?
+ HandlerErrorCode.AlreadyExists :
+ HandlerErrorCode.ResourceConflict;
+
+ return ProgressEvent.defaultFailureHandler(exception, handlerErrorCode);
+
+ } else {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.GeneralServiceException);
+ }
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/DeleteHandler.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/DeleteHandler.java
new file mode 100644
index 0000000..6799150
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/DeleteHandler.java
@@ -0,0 +1,73 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.awssdk.awscore.AwsResponse;
+import software.amazon.awssdk.awscore.exception.AwsServiceException;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.awssdk.services.redshiftserverless.model.ConflictException;
+import software.amazon.awssdk.services.redshiftserverless.model.DeleteEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.DeleteEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.InternalServerException;
+import software.amazon.awssdk.services.redshiftserverless.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.redshiftserverless.model.ValidationException;
+import software.amazon.cloudformation.exceptions.CfnGeneralServiceException;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.HandlerErrorCode;
+import software.amazon.cloudformation.proxy.Logger;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+
+public class DeleteHandler extends BaseHandlerStd {
+ private Logger logger;
+
+ protected ProgressEvent handleRequest(
+ final AmazonWebServicesClientProxy proxy,
+ final ResourceHandlerRequest request,
+ final CallbackContext callbackContext,
+ final ProxyClient proxyClient,
+ final Logger logger) {
+
+ this.logger = logger;
+ final ResourceModel resourceModel = request.getDesiredResourceState();
+
+ return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext)
+ .then(progress ->
+ proxy.initiate("AWS-RedshiftServerless-EndpointAccess::Delete", proxyClient, progress.getResourceModel(), progress.getCallbackContext())
+ .translateToServiceRequest(Translator::translateToDeleteEndpointAccessRequest)
+ .makeServiceCall(this::deleteEndpointAccess)
+ .stabilize(this::isEndpointAccessRequest)
+ .handleError(this::deleteEndpointAccessErrorHandler)
+ .done(awsResponse -> ProgressEvent.defaultSuccessHandler(Translator.translateFromEndpointAccessResponse(awsResponse.endpoint()))));
+ }
+
+ private DeleteEndpointAccessResponse deleteEndpointAccess(final DeleteEndpointAccessRequest awsRequest,
+ final ProxyClient proxyClient) {
+ DeleteEndpointAccessResponse awsResponse;
+ awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest, proxyClient.client()::deleteEndpointAccess);
+
+ logger.log(String.format("%s successfully deleted.", ResourceModel.TYPE_NAME));
+ return awsResponse;
+ }
+
+ private ProgressEvent deleteEndpointAccessErrorHandler(final DeleteEndpointAccessRequest awsRequest,
+ final Exception exception,
+ final ProxyClient client,
+ final ResourceModel model,
+ final CallbackContext context) {
+ if (exception instanceof ResourceNotFoundException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.NotFound);
+
+ } else if (exception instanceof InternalServerException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.InternalFailure);
+
+ } else if (exception instanceof ConflictException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.ResourceConflict);
+
+ } else if (exception instanceof ValidationException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.InvalidRequest);
+
+ } else {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.GeneralServiceException);
+ }
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ListHandler.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ListHandler.java
new file mode 100644
index 0000000..77f68d4
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ListHandler.java
@@ -0,0 +1,73 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.awssdk.awscore.AwsRequest;
+import software.amazon.awssdk.awscore.AwsResponse;
+import software.amazon.awssdk.awscore.exception.AwsServiceException;
+import software.amazon.awssdk.services.redshiftserverless.model.InternalServerException;
+import software.amazon.awssdk.services.redshiftserverless.model.ConflictException;
+import software.amazon.awssdk.services.redshiftserverless.model.ListEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.ListEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.redshiftserverless.model.ValidationException;
+import software.amazon.cloudformation.exceptions.CfnGeneralServiceException;
+import software.amazon.cloudformation.exceptions.CfnInternalFailureException;
+import software.amazon.cloudformation.exceptions.CfnInvalidRequestException;
+import software.amazon.cloudformation.exceptions.CfnResourceConflictException;
+import software.amazon.cloudformation.exceptions.CfnNotFoundException;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.Logger;
+import software.amazon.cloudformation.proxy.OperationStatus;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ListHandler extends BaseHandler {
+ private Logger logger;
+
+ @Override
+ public ProgressEvent handleRequest(
+ final AmazonWebServicesClientProxy proxy,
+ final ResourceHandlerRequest request,
+ final CallbackContext callbackContext,
+ final Logger logger) {
+
+ this.logger = logger;
+ final ResourceModel resourceModel = request.getDesiredResourceState();
+
+ ListEndpointAccessRequest awsRequest = Translator.translateToListEndpointAccessRequest(resourceModel, request.getNextToken());
+ ListEndpointAccessResponse awsResponse = listEndpointAccess(awsRequest, proxy);
+ List models = Translator.translateFromListEndpointAccessResponse(awsResponse);
+
+ return ProgressEvent.builder()
+ .resourceModels(models)
+ .nextToken(awsResponse.nextToken())
+ .status(OperationStatus.SUCCESS)
+ .build();
+ }
+
+ private ListEndpointAccessResponse listEndpointAccess(final ListEndpointAccessRequest awsRequest,
+ final AmazonWebServicesClientProxy proxy) {
+ ListEndpointAccessResponse awsResponse;
+ try {
+ awsResponse = proxy.injectCredentialsAndInvokeV2(awsRequest, ClientBuilder.getClient()::listEndpointAccess);
+
+ } catch (final ValidationException e) {
+ throw new CfnInvalidRequestException(e);
+
+ } catch (final InternalServerException e) {
+ throw new CfnInternalFailureException(e);
+
+ } catch(final ConflictException e) {
+ throw new CfnResourceConflictException(e);
+
+ } catch(final ResourceNotFoundException e) {
+ throw new CfnNotFoundException(e);
+
+ }
+
+ logger.log(String.format("%s has successfully been listed.", ResourceModel.TYPE_NAME));
+ return awsResponse;
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ReadHandler.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ReadHandler.java
new file mode 100644
index 0000000..935c01c
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/ReadHandler.java
@@ -0,0 +1,70 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.awssdk.awscore.AwsResponse;
+import software.amazon.awssdk.awscore.exception.AwsServiceException;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.awssdk.services.redshiftserverless.model.GetEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.GetEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.InternalServerException;
+import software.amazon.awssdk.services.redshiftserverless.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.redshiftserverless.model.ValidationException;
+import software.amazon.cloudformation.exceptions.CfnGeneralServiceException;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.HandlerErrorCode;
+import software.amazon.cloudformation.proxy.Logger;
+import software.amazon.cloudformation.proxy.OperationStatus;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+
+public class ReadHandler extends BaseHandlerStd {
+ private Logger logger;
+
+ protected ProgressEvent handleRequest(
+ final AmazonWebServicesClientProxy proxy,
+ final ResourceHandlerRequest request,
+ final CallbackContext callbackContext,
+ final ProxyClient proxyClient,
+ final Logger logger) {
+
+ this.logger = logger;
+ final ResourceModel model = request.getDesiredResourceState();
+
+ return ProgressEvent.progress(model, callbackContext)
+ .then(progress ->
+ proxy.initiate("AWS-RedshiftServerless-EndpointAccess::Read", proxyClient, progress.getResourceModel(), progress.getCallbackContext())
+ .translateToServiceRequest(Translator::translateToGetEndpointAccessRequest)
+ .makeServiceCall(this::getEndpointAccess)
+ .stabilize(this::isEndpointAccessRequest)
+ .handleError(this::getEndpointAccessErrorHandler)
+ .done(awsResponse -> ProgressEvent.defaultSuccessHandler(Translator.translateFromEndpointAccessResponse(awsResponse.endpoint()))));
+ }
+
+ private GetEndpointAccessResponse getEndpointAccess(final GetEndpointAccessRequest awsRequest,
+ final ProxyClient proxyClient) {
+ GetEndpointAccessResponse awsResponse;
+ awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest, proxyClient.client()::getEndpointAccess);
+
+ logger.log(String.format("%s has successfully been read.", ResourceModel.TYPE_NAME));
+ return awsResponse;
+ }
+
+ private ProgressEvent getEndpointAccessErrorHandler(final GetEndpointAccessRequest awsRequest,
+ final Exception exception,
+ final ProxyClient client,
+ final ResourceModel model,
+ final CallbackContext context) {
+ if (exception instanceof ResourceNotFoundException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.NotFound);
+
+ } else if (exception instanceof ValidationException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.InvalidRequest);
+
+ } else if (exception instanceof InternalServerException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.InternalFailure);
+
+ } else {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.GeneralServiceException);
+ }
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/Translator.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/Translator.java
new file mode 100644
index 0000000..3dd449c
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/Translator.java
@@ -0,0 +1,147 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import software.amazon.awssdk.awscore.AwsRequest;
+import software.amazon.awssdk.awscore.AwsResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.CreateEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.DeleteEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.EndpointAccess;
+import software.amazon.awssdk.services.redshiftserverless.model.GetEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.ListEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.ListEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.UpdateEndpointAccessRequest;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * This class is a centralized placeholder for
+ * - api request construction
+ * - object translation to/from aws sdk
+ * - resource model construction for read/list handlers
+ */
+
+public class Translator {
+ private static final Gson GSON = new GsonBuilder().create();
+
+ /**
+ * Request to create endpoint access
+ *
+ * @param model resource model
+ * @return awsRequest the aws service request to create endpoint access
+ */
+ static CreateEndpointAccessRequest translateToCreateEndpointAccessRequest(final ResourceModel model) {
+ return CreateEndpointAccessRequest.builder()
+ .endpointName(model.getEndpointName())
+ .ownerAccount(model.getOwnerAccount())
+ .vpcSecurityGroupIds(model.getVpcSecurityGroupIds())
+ .subnetIds(model.getSubnetIds())
+ .workgroupName(model.getWorkgroupName())
+ .build();
+ }
+
+ /**
+ * Translates EndpointAccess resource object from sdk into a resource model
+ *
+ * @param endpointAccess the aws services resource response
+ * @return model resource model
+ */
+ static ResourceModel translateFromEndpointAccessResponse(final EndpointAccess endpointAccess) {
+ return ResourceModel.builder()
+ .endpointName(endpointAccess.endpointName())
+ .endpointStatus(endpointAccess.endpointStatus())
+ .workgroupName(endpointAccess.workgroupName())
+ .endpointCreateTime(endpointAccess.endpointCreateTime().toString())
+ .port(endpointAccess.port())
+ .address(endpointAccess.address())
+ .subnetIds(endpointAccess.subnetIds())
+ .vpcSecurityGroups(translateToModelVpcSecurityGroupMemberships(endpointAccess.vpcSecurityGroups()))
+ .vpcEndpoint(translateToModelVpcEndpoint(endpointAccess.vpcEndpoint()))
+ .endpointArn(endpointAccess.endpointArn())
+ .build();
+ }
+
+ /**
+ * Request to get endpoint access
+ *
+ * @param model resource model
+ * @return awsRequest the aws service request to delete a resource
+ */
+ static GetEndpointAccessRequest translateToGetEndpointAccessRequest(final ResourceModel model) {
+ return GetEndpointAccessRequest.builder()
+ .endpointName(model.getEndpointName())
+ .build();
+ }
+
+ /**
+ * Request to delete endpoint access
+ *
+ * @param model resource model
+ * @return awsRequest the aws service request to delete a resource
+ */
+ static DeleteEndpointAccessRequest translateToDeleteEndpointAccessRequest(final ResourceModel model) {
+ return DeleteEndpointAccessRequest.builder()
+ .endpointName(model.getEndpointName())
+ .build();
+ }
+
+ /**
+ * Request to update properties of a previously created endpoint access
+ *
+ * @param model resource model
+ * @return awsRequest the aws service request to modify a resource
+ */
+ static UpdateEndpointAccessRequest translateToUpdateEndpointAccessRequest(final ResourceModel model) {
+ return UpdateEndpointAccessRequest.builder()
+ .endpointName(model.getEndpointName())
+ .vpcSecurityGroupIds(model.getVpcSecurityGroupIds())
+ .build();
+ }
+
+ /**
+ * Request to list resources
+ * @param nextToken token passed to the aws service list resources request
+ * @return awsRequest the aws service request to list resources within aws account
+ */
+ static ListEndpointAccessRequest translateToListEndpointAccessRequest(final ResourceModel resourceModel, final String nextToken) {
+ return ListEndpointAccessRequest.builder()
+ .workgroupName(resourceModel.getWorkgroupName())
+ .ownerAccount(resourceModel.getOwnerAccount())
+ .vpcId(resourceModel.getVpcId())
+ .build();
+ }
+
+ /**
+ * Translates resource objects from sdk into a resource model (primary identifier only)
+ *
+ * @param awsResponse the aws service describe resource response
+ * @return list of resource models
+ */
+ static List translateFromListEndpointAccessResponse(final ListEndpointAccessResponse awsResponse) {
+ return awsResponse.endpoints()
+ .stream()
+ .map(endpointAccess -> translateFromEndpointAccessResponse(endpointAccess))
+ .collect(Collectors.toList());
+ }
+
+
+ private static VpcEndpoint translateToModelVpcEndpoint(software.amazon.awssdk.services.redshiftserverless.model.VpcEndpoint vpcEndpoint) {
+ return GSON.fromJson(GSON.toJson(vpcEndpoint), VpcEndpoint.class);
+ }
+
+ private static VpcSecurityGroupMembership translateToModelVpcSecurityGroupMembership(software.amazon.awssdk.services.redshiftserverless.model.VpcSecurityGroupMembership vpcSecurityGroupMembership) {
+ return GSON.fromJson(GSON.toJson(vpcSecurityGroupMembership), VpcSecurityGroupMembership.class);
+ }
+
+ private static List translateToModelVpcSecurityGroupMemberships(Collection vpcSecurityGroupMemberships) {
+ return vpcSecurityGroupMemberships == null ? null : vpcSecurityGroupMemberships
+ .stream()
+ .map(Translator::translateToModelVpcSecurityGroupMembership)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/UpdateHandler.java b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/UpdateHandler.java
new file mode 100644
index 0000000..a04102f
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/main/java/software/amazon/redshiftserverless/endpointaccess/UpdateHandler.java
@@ -0,0 +1,78 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.awssdk.awscore.AwsResponse;
+import software.amazon.awssdk.awscore.exception.AwsServiceException;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.awssdk.services.redshiftserverless.model.AccessDeniedException;
+import software.amazon.awssdk.services.redshiftserverless.model.ConflictException;
+import software.amazon.awssdk.services.redshiftserverless.model.InsufficientCapacityException;
+import software.amazon.awssdk.services.redshiftserverless.model.InternalServerException;
+import software.amazon.awssdk.services.redshiftserverless.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.redshiftserverless.model.UpdateEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.model.UpdateEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.ValidationException;
+import software.amazon.cloudformation.exceptions.CfnGeneralServiceException;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.HandlerErrorCode;
+import software.amazon.cloudformation.proxy.Logger;
+import software.amazon.cloudformation.proxy.OperationStatus;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+
+public class UpdateHandler extends BaseHandlerStd {
+ private Logger logger;
+
+ protected ProgressEvent handleRequest(
+ final AmazonWebServicesClientProxy proxy,
+ final ResourceHandlerRequest request,
+ final CallbackContext callbackContext,
+ final ProxyClient proxyClient,
+ final Logger logger) {
+
+ this.logger = logger;
+ final ResourceModel model = request.getDesiredResourceState();
+
+ return ProgressEvent.progress(model, callbackContext)
+ .then(progress ->
+ proxy.initiate("AWS-RedshiftServerless-EndpointAccess::Update", proxyClient, progress.getResourceModel(), progress.getCallbackContext())
+ .translateToServiceRequest(Translator::translateToUpdateEndpointAccessRequest)
+ .makeServiceCall(this::updateEndpointAccess)
+ .stabilize(this::isEndpointAccessRequest)
+ .handleError(this::updateEndpointAccessErrorHandler)
+ .done(awsResponse -> ProgressEvent.defaultSuccessHandler(Translator.translateFromEndpointAccessResponse(awsResponse.endpoint()))));
+ }
+
+ private UpdateEndpointAccessResponse updateEndpointAccess(final UpdateEndpointAccessRequest awsRequest,
+ final ProxyClient proxyClient) {
+ UpdateEndpointAccessResponse awsResponse;
+ awsResponse = proxyClient.injectCredentialsAndInvokeV2(awsRequest, proxyClient.client()::updateEndpointAccess);
+
+ logger.log(String.format("%s has successfully been updated.", ResourceModel.TYPE_NAME));
+ return awsResponse;
+ }
+
+ private ProgressEvent updateEndpointAccessErrorHandler(final UpdateEndpointAccessRequest awsRequest,
+ final Exception exception,
+ final ProxyClient client,
+ final ResourceModel model,
+ final CallbackContext context) {
+ if (exception instanceof ResourceNotFoundException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.NotFound);
+
+ } else if (exception instanceof ValidationException ||
+ exception instanceof AccessDeniedException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.InvalidRequest);
+
+ } else if (exception instanceof InternalServerException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.InternalFailure);
+
+ } else if (exception instanceof ConflictException ||
+ exception instanceof InsufficientCapacityException) {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.ResourceConflict);
+
+ } else {
+ return ProgressEvent.defaultFailureHandler(exception, HandlerErrorCode.GeneralServiceException);
+ }
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/resources/log4j2.xml b/aws-redshiftserverless-endpointaccess/src/resources/log4j2.xml
new file mode 100644
index 0000000..5657daf
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/resources/log4j2.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/AbstractTestBase.java b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/AbstractTestBase.java
new file mode 100644
index 0000000..5a5cc4a
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/AbstractTestBase.java
@@ -0,0 +1,253 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import java.time.Instant;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+import software.amazon.awssdk.awscore.AwsRequest;
+import software.amazon.awssdk.awscore.AwsResponse;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.awssdk.core.ResponseBytes;
+import software.amazon.awssdk.core.ResponseInputStream;
+import software.amazon.awssdk.core.pagination.sync.SdkIterable;
+import software.amazon.awssdk.services.redshiftserverless.model.CreateEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.DeleteEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.GetEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.ListEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.UpdateEndpointAccessResponse;
+import software.amazon.awssdk.services.redshiftserverless.model.EndpointAccess;
+import software.amazon.awssdk.services.redshiftserverless.model.VpcEndpoint;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.Credentials;
+import software.amazon.cloudformation.proxy.LoggerProxy;
+import software.amazon.cloudformation.proxy.ProxyClient;
+
+public class AbstractTestBase {
+ protected static final Credentials MOCK_CREDENTIALS;
+ protected static final LoggerProxy logger;
+ protected static final String AWS_REGION;
+ private static final String ENDPOINT_NAME;
+ private static final String ENDPOINT_STATUS;
+ private static final String WORKGROUP_NAME;
+ private static final int DEFAULT_PORT;
+ private static final String ADDRESS;
+ private static final List SUBNET_IDS;
+ private static final List VPC_SECURITY_GROUPS;
+ private static final String ENDPOINT_ARN;
+ private static final String VPC_ENDPOINT_ID;
+ private static final String VPC_ID;
+ private static final Instant CREATION_DATE;
+ private static final String OWNER_ACCOUNT;
+
+ static {
+ MOCK_CREDENTIALS = new Credentials("accessKey", "secretKey", "token");
+ logger = new LoggerProxy();
+ AWS_REGION = "us-east-1";
+ ENDPOINT_NAME = "testendpoint";
+ ENDPOINT_STATUS = "available";
+ WORKGROUP_NAME = "testworkgroup";
+ DEFAULT_PORT = 5439;
+ ADDRESS = "xyz";
+ SUBNET_IDS = Collections.emptyList();
+ VPC_SECURITY_GROUPS = Collections.emptyList();
+ ENDPOINT_ARN = "abc";
+ VPC_ENDPOINT_ID = "def";
+ VPC_ID = "ghi";
+ CREATION_DATE = Instant.now();
+ OWNER_ACCOUNT = "abcd";
+ }
+ static ProxyClient MOCK_PROXY(
+ final AmazonWebServicesClientProxy proxy,
+ final RedshiftServerlessClient sdkClient) {
+ return new ProxyClient() {
+ @Override
+ public ResponseT
+ injectCredentialsAndInvokeV2(RequestT request, Function requestFunction) {
+ return proxy.injectCredentialsAndInvokeV2(request, requestFunction);
+ }
+
+ @Override
+ public
+ CompletableFuture
+ injectCredentialsAndInvokeV2Async(RequestT request, Function> requestFunction) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public >
+ IterableT
+ injectCredentialsAndInvokeIterableV2(RequestT request, Function requestFunction) {
+ return proxy.injectCredentialsAndInvokeIterableV2(request, requestFunction);
+ }
+
+ @Override
+ public ResponseInputStream
+ injectCredentialsAndInvokeV2InputStream(RequestT requestT, Function> function) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ResponseBytes
+ injectCredentialsAndInvokeV2Bytes(RequestT requestT, Function> function) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public RedshiftServerlessClient client() {
+ return sdkClient;
+ }
+ };
+ }
+
+ public static ResourceModel createEndpointAccessResourceModel() {
+ return ResourceModel.builder()
+ .endpointName(ENDPOINT_NAME)
+ .subnetIds(SUBNET_IDS)
+ .workgroupName(WORKGROUP_NAME)
+ .build();
+ }
+
+ public static CreateEndpointAccessResponse createEndpointAccessResponseSdk() {
+ return CreateEndpointAccessResponse.builder()
+ .endpoint(EndpointAccess.builder()
+ .endpointName(ENDPOINT_NAME)
+ .endpointArn(ENDPOINT_ARN)
+ .endpointCreateTime(CREATION_DATE)
+ .endpointStatus(ENDPOINT_STATUS)
+ .address(ADDRESS)
+ .port(DEFAULT_PORT)
+ .subnetIds(SUBNET_IDS)
+ .workgroupName(WORKGROUP_NAME)
+ .vpcSecurityGroups(VPC_SECURITY_GROUPS)
+ .vpcEndpoint(VpcEndpoint.builder().vpcEndpointId(VPC_ENDPOINT_ID).vpcId(VPC_ID).networkInterfaces(Collections.emptyList()).build())
+ .build())
+ .build();
+ }
+
+ public static ResourceModel deleteEndpointAccessResourceModel() {
+ return ResourceModel.builder()
+ .endpointName(ENDPOINT_NAME)
+ .build();
+ }
+
+ public static DeleteEndpointAccessResponse deleteEndpointAccessResponseSdk() {
+ return DeleteEndpointAccessResponse.builder()
+ .endpoint(EndpointAccess.builder()
+ .endpointName(ENDPOINT_NAME)
+ .endpointArn(ENDPOINT_ARN)
+ .endpointCreateTime(CREATION_DATE)
+ .endpointStatus(ENDPOINT_STATUS)
+ .address(ADDRESS)
+ .port(DEFAULT_PORT)
+ .subnetIds(SUBNET_IDS)
+ .workgroupName(WORKGROUP_NAME)
+ .vpcSecurityGroups(VPC_SECURITY_GROUPS)
+ .vpcEndpoint(VpcEndpoint.builder().vpcEndpointId(VPC_ENDPOINT_ID).vpcId(VPC_ID).networkInterfaces(Collections.emptyList()).build())
+ .build())
+ .build();
+ }
+
+ public static ResourceModel getEndpointAccessResourceModel() {
+ return ResourceModel.builder()
+ .endpointName(ENDPOINT_NAME)
+ .build();
+ }
+
+ public static GetEndpointAccessResponse getEndpointAccessResponseSdk() {
+ return GetEndpointAccessResponse.builder()
+ .endpoint(EndpointAccess.builder()
+ .endpointName(ENDPOINT_NAME)
+ .endpointArn(ENDPOINT_ARN)
+ .endpointCreateTime(CREATION_DATE)
+ .endpointStatus(ENDPOINT_STATUS)
+ .address(ADDRESS)
+ .port(DEFAULT_PORT)
+ .subnetIds(SUBNET_IDS)
+ .workgroupName(WORKGROUP_NAME)
+ .vpcSecurityGroups(VPC_SECURITY_GROUPS)
+ .vpcEndpoint(VpcEndpoint.builder().vpcEndpointId(VPC_ENDPOINT_ID).vpcId(VPC_ID).networkInterfaces(Collections.emptyList()).build())
+ .build())
+ .build();
+ }
+
+ public static ResourceModel listEndpointAccessResourceModel() {
+ return ResourceModel.builder()
+ .workgroupName(WORKGROUP_NAME)
+ .vpcId(VPC_ID)
+ .ownerAccount(OWNER_ACCOUNT)
+ .build();
+ }
+
+ public static ListEndpointAccessResponse getListEndpointAccessResponsesSdk() {
+ return ListEndpointAccessResponse.builder()
+ .endpoints(software.amazon.awssdk.services.redshiftserverless.model.EndpointAccess.builder()
+ .endpointName(ENDPOINT_NAME)
+ .endpointArn(ENDPOINT_ARN)
+ .endpointCreateTime(CREATION_DATE)
+ .endpointStatus(ENDPOINT_STATUS)
+ .address(ADDRESS)
+ .port(DEFAULT_PORT)
+ .subnetIds(SUBNET_IDS)
+ .workgroupName(WORKGROUP_NAME)
+ .vpcSecurityGroups(VPC_SECURITY_GROUPS)
+ .vpcEndpoint(VpcEndpoint.builder().vpcEndpointId(VPC_ENDPOINT_ID).vpcId(VPC_ID).networkInterfaces(Collections.emptyList()).build())
+ .build())
+ .build();
+ }
+
+ public static List getListResponsesResourceModel() {
+ return Collections.singletonList(ResourceModel.builder()
+ .endpointName(ENDPOINT_NAME)
+ .endpointArn(ENDPOINT_ARN)
+ .endpointCreateTime(CREATION_DATE.toString())
+ .endpointStatus(ENDPOINT_STATUS)
+ .address(ADDRESS)
+ .port(DEFAULT_PORT)
+ .subnetIds(SUBNET_IDS)
+ .workgroupName(WORKGROUP_NAME)
+ .vpcSecurityGroups(Collections.emptyList())
+ .vpcEndpoint(software.amazon.redshiftserverless.endpointaccess.VpcEndpoint.builder().vpcEndpointId(VPC_ENDPOINT_ID).vpcId(VPC_ID).networkInterfaces(Collections.emptyList()).build())
+ .build());
+ }
+
+ public static ResourceModel updateEndpointAccessResourceModel() {
+ return ResourceModel.builder()
+ .endpointName(ENDPOINT_NAME)
+ .vpcSecurityGroups(Collections.emptyList())
+ .build();
+ }
+
+ public static UpdateEndpointAccessResponse updateEndpointAccessResponseSdk() {
+ return UpdateEndpointAccessResponse.builder()
+ .endpoint(EndpointAccess.builder()
+ .endpointName(ENDPOINT_NAME)
+ .endpointArn(ENDPOINT_ARN)
+ .endpointCreateTime(CREATION_DATE)
+ .endpointStatus(ENDPOINT_STATUS)
+ .address(ADDRESS)
+ .port(DEFAULT_PORT)
+ .subnetIds(SUBNET_IDS)
+ .workgroupName(WORKGROUP_NAME)
+ .vpcSecurityGroups(VPC_SECURITY_GROUPS)
+ .vpcEndpoint(VpcEndpoint.builder().vpcEndpointId(VPC_ENDPOINT_ID).vpcId(VPC_ID).networkInterfaces(Collections.emptyList()).build())
+ .build())
+ .build();
+ }
+
+ public static ResourceModel getEndpointAccessResponseResourceModel() {
+ return ResourceModel.builder()
+ .endpointName(ENDPOINT_NAME)
+ .endpointArn(ENDPOINT_ARN)
+ .endpointCreateTime(CREATION_DATE.toString())
+ .endpointStatus(ENDPOINT_STATUS)
+ .address(ADDRESS)
+ .port(DEFAULT_PORT)
+ .subnetIds(SUBNET_IDS)
+ .workgroupName(WORKGROUP_NAME)
+ .vpcSecurityGroups(Collections.emptyList())
+ .vpcEndpoint(software.amazon.redshiftserverless.endpointaccess.VpcEndpoint.builder().vpcEndpointId(VPC_ENDPOINT_ID).vpcId(VPC_ID).networkInterfaces(Collections.emptyList()).build())
+ .build();
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/CreateHandlerTest.java b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/CreateHandlerTest.java
new file mode 100644
index 0000000..8a1356e
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/CreateHandlerTest.java
@@ -0,0 +1,74 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import java.time.Duration;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.awssdk.services.redshiftserverless.model.CreateEndpointAccessRequest;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.OperationStatus;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class CreateHandlerTest extends AbstractTestBase {
+
+ @Mock
+ private AmazonWebServicesClientProxy proxy;
+
+ @Mock
+ private ProxyClient proxyClient;
+
+ @Mock
+ RedshiftServerlessClient sdkClient;
+
+ @BeforeEach
+ public void setup() {
+ proxy = new AmazonWebServicesClientProxy(logger, MOCK_CREDENTIALS, () -> Duration.ofSeconds(600).toMillis());
+ sdkClient = mock(RedshiftServerlessClient.class);
+ proxyClient = MOCK_PROXY(proxy, sdkClient);
+ }
+
+ @AfterEach
+ public void tear_down() {
+ verify(sdkClient, atLeastOnce()).serviceName();
+ verifyNoMoreInteractions(sdkClient);
+ }
+
+ @Test
+ public void handleRequest_SimpleSuccess() {
+ final CreateHandler handler = new CreateHandler();
+
+ final ResourceModel requestResourceModel = createEndpointAccessResourceModel();
+ final ResourceModel responseResourceModel = getEndpointAccessResponseResourceModel();
+
+ final ResourceHandlerRequest request = ResourceHandlerRequest.builder()
+ .desiredResourceState(requestResourceModel)
+ .build();
+
+ when(proxyClient.client().createEndpointAccess(any(CreateEndpointAccessRequest.class))).thenReturn(createEndpointAccessResponseSdk());
+
+ final ProgressEvent response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
+
+ assertThat(response).isNotNull();
+ assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS);
+ assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
+ assertThat(response.getResourceModel()).isEqualTo(responseResourceModel);
+ assertThat(response.getResourceModels()).isNull();
+ assertThat(response.getMessage()).isNull();
+ assertThat(response.getErrorCode()).isNull();
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/DeleteHandlerTest.java b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/DeleteHandlerTest.java
new file mode 100644
index 0000000..4591bfc
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/DeleteHandlerTest.java
@@ -0,0 +1,74 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import java.time.Duration;
+import software.amazon.awssdk.services.redshiftserverless.model.DeleteEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.OperationStatus;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class DeleteHandlerTest extends AbstractTestBase {
+
+ @Mock
+ private AmazonWebServicesClientProxy proxy;
+
+ @Mock
+ private ProxyClient proxyClient;
+
+ @Mock
+ RedshiftServerlessClient sdkClient;
+
+ @BeforeEach
+ public void setup() {
+ proxy = new AmazonWebServicesClientProxy(logger, MOCK_CREDENTIALS, () -> Duration.ofSeconds(600).toMillis());
+ sdkClient = mock(RedshiftServerlessClient.class);
+ proxyClient = MOCK_PROXY(proxy, sdkClient);
+ }
+
+ @AfterEach
+ public void tear_down() {
+ verify(sdkClient, atLeastOnce()).serviceName();
+ verifyNoMoreInteractions(sdkClient);
+ }
+
+ @Test
+ public void handleRequest_SimpleSuccess() {
+ final DeleteHandler handler = new DeleteHandler();
+
+ final ResourceModel requestResourceModel = deleteEndpointAccessResourceModel();
+ final ResourceModel responseResourceModel = getEndpointAccessResponseResourceModel();
+
+ final ResourceHandlerRequest request = ResourceHandlerRequest.builder()
+ .desiredResourceState(requestResourceModel)
+ .build();
+
+ when(proxyClient.client().deleteEndpointAccess(any(DeleteEndpointAccessRequest.class))).thenReturn(deleteEndpointAccessResponseSdk());
+
+ final ProgressEvent response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
+
+ assertThat(response).isNotNull();
+ assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS);
+ assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
+ assertThat(response.getResourceModel()).isEqualTo(responseResourceModel);
+ assertThat(response.getResourceModels()).isNull();
+ assertThat(response.getMessage()).isNull();
+ assertThat(response.getErrorCode()).isNull();
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/ListHandlerTest.java b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/ListHandlerTest.java
new file mode 100644
index 0000000..1db32eb
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/ListHandlerTest.java
@@ -0,0 +1,62 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.Logger;
+import software.amazon.cloudformation.proxy.OperationStatus;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.doReturn;
+
+@ExtendWith(MockitoExtension.class)
+public class ListHandlerTest extends AbstractTestBase {
+
+ @Mock
+ private AmazonWebServicesClientProxy proxy;
+
+ @Mock
+ private Logger logger;
+
+ @BeforeEach
+ public void setup() {
+ proxy = mock(AmazonWebServicesClientProxy.class);
+ logger = mock(Logger.class);
+ System.setProperty("aws.region", AWS_REGION);
+ }
+
+ @Test
+ public void handleRequest_SimpleSuccess() {
+ final ListHandler handler = new ListHandler();
+
+ final ResourceModel requestResourceModel = listEndpointAccessResourceModel();
+ final List responseResourceModel = getListResponsesResourceModel();
+
+ final ResourceHandlerRequest request = ResourceHandlerRequest.builder()
+ .desiredResourceState(requestResourceModel)
+ .build();
+
+ doReturn(getListEndpointAccessResponsesSdk()).when(proxy).injectCredentialsAndInvokeV2(any(), any());
+
+ final ProgressEvent response =
+ handler.handleRequest(proxy, request, null, logger);
+
+ assertThat(response).isNotNull();
+ assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS);
+ assertThat(response.getCallbackContext()).isNull();
+ assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
+ assertThat(response.getResourceModel()).isNull();
+ assertThat(response.getResourceModels()).isEqualTo(responseResourceModel);
+ assertThat(response.getMessage()).isNull();
+ assertThat(response.getErrorCode()).isNull();
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/ReadHandlerTest.java b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/ReadHandlerTest.java
new file mode 100644
index 0000000..5f04f67
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/ReadHandlerTest.java
@@ -0,0 +1,74 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import java.time.Duration;
+import software.amazon.awssdk.services.redshiftserverless.model.GetEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.OperationStatus;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ReadHandlerTest extends AbstractTestBase {
+
+ @Mock
+ private AmazonWebServicesClientProxy proxy;
+
+ @Mock
+ private ProxyClient proxyClient;
+
+ @Mock
+ RedshiftServerlessClient sdkClient;
+
+ @BeforeEach
+ public void setup() {
+ proxy = new AmazonWebServicesClientProxy(logger, MOCK_CREDENTIALS, () -> Duration.ofSeconds(600).toMillis());
+ sdkClient = mock(RedshiftServerlessClient.class);
+ proxyClient = MOCK_PROXY(proxy, sdkClient);
+ }
+
+ @AfterEach
+ public void tear_down() {
+ verify(sdkClient, atLeastOnce()).serviceName();
+ verifyNoMoreInteractions(sdkClient);
+ }
+
+ @Test
+ public void handleRequest_SimpleSuccess() {
+ final ReadHandler handler = new ReadHandler();
+
+ final ResourceModel requestResourceModel = getEndpointAccessResourceModel();
+ final ResourceModel responseResourceModel = getEndpointAccessResponseResourceModel();
+
+ final ResourceHandlerRequest request = ResourceHandlerRequest.builder()
+ .desiredResourceState(requestResourceModel)
+ .build();
+
+ when(proxyClient.client().getEndpointAccess(any(GetEndpointAccessRequest.class))).thenReturn(getEndpointAccessResponseSdk());
+
+ final ProgressEvent response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
+
+ assertThat(response).isNotNull();
+ assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS);
+ assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
+ assertThat(response.getResourceModel()).isEqualTo(responseResourceModel);
+ assertThat(response.getResourceModels()).isNull();
+ assertThat(response.getMessage()).isNull();
+ assertThat(response.getErrorCode()).isNull();
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/UpdateHandlerTest.java b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/UpdateHandlerTest.java
new file mode 100644
index 0000000..eebac17
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/src/test/java/software/amazon/redshiftserverless/endpointaccess/UpdateHandlerTest.java
@@ -0,0 +1,74 @@
+package software.amazon.redshiftserverless.endpointaccess;
+
+import java.time.Duration;
+import software.amazon.awssdk.services.redshiftserverless.model.UpdateEndpointAccessRequest;
+import software.amazon.awssdk.services.redshiftserverless.RedshiftServerlessClient;
+import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
+import software.amazon.cloudformation.proxy.OperationStatus;
+import software.amazon.cloudformation.proxy.ProgressEvent;
+import software.amazon.cloudformation.proxy.ProxyClient;
+import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class UpdateHandlerTest extends AbstractTestBase {
+
+ @Mock
+ private AmazonWebServicesClientProxy proxy;
+
+ @Mock
+ private ProxyClient proxyClient;
+
+ @Mock
+ RedshiftServerlessClient sdkClient;
+
+ @BeforeEach
+ public void setup() {
+ proxy = new AmazonWebServicesClientProxy(logger, MOCK_CREDENTIALS, () -> Duration.ofSeconds(600).toMillis());
+ sdkClient = mock(RedshiftServerlessClient.class);
+ proxyClient = MOCK_PROXY(proxy, sdkClient);
+ }
+
+ @AfterEach
+ public void tear_down() {
+ verify(sdkClient, atLeastOnce()).serviceName();
+ verifyNoMoreInteractions(sdkClient);
+ }
+
+ @Test
+ public void handleRequest_SimpleSuccess() {
+ final UpdateHandler handler = new UpdateHandler();
+
+ final ResourceModel requestResourceModel = updateEndpointAccessResourceModel();
+ final ResourceModel responseResourceModel = getEndpointAccessResponseResourceModel();
+
+ final ResourceHandlerRequest request = ResourceHandlerRequest.builder()
+ .desiredResourceState(requestResourceModel)
+ .build();
+
+ when(proxyClient.client().updateEndpointAccess(any(UpdateEndpointAccessRequest.class))).thenReturn(updateEndpointAccessResponseSdk());
+
+ final ProgressEvent response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
+
+ assertThat(response).isNotNull();
+ assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS);
+ assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
+ assertThat(response.getResourceModel()).isEqualTo(responseResourceModel);
+ assertThat(response.getResourceModels()).isNull();
+ assertThat(response.getMessage()).isNull();
+ assertThat(response.getErrorCode()).isNull();
+ }
+}
diff --git a/aws-redshiftserverless-endpointaccess/template.yml b/aws-redshiftserverless-endpointaccess/template.yml
new file mode 100644
index 0000000..abdac4c
--- /dev/null
+++ b/aws-redshiftserverless-endpointaccess/template.yml
@@ -0,0 +1,24 @@
+AWSTemplateFormatVersion: "2010-09-09"
+Transform: AWS::Serverless-2016-10-31
+Description: AWS SAM template for the AWS::RedshiftServerless::EndpointAccess resource type
+
+Globals:
+ Function:
+ Timeout: 180 # docker start-up times can be long for SAM CLI
+ MemorySize: 512
+
+Resources:
+ TypeFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: software.amazon.redshiftserverless.endpointaccess.HandlerWrapper::handleRequest
+ Runtime: java8
+ CodeUri: ./target/aws-redshiftserverless-endpointaccess-handler-1.0-SNAPSHOT.jar
+
+ TestEntrypoint:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: software.amazon.redshiftserverless.endpointaccess.HandlerWrapper::testEntrypoint
+ Runtime: java8
+ CodeUri: ./target/aws-redshiftserverless-endpointaccess-handler-1.0-SNAPSHOT.jar
+