From ae8b5b04ebf85aec513a9244ef3e757650826dc2 Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Fri, 24 Jul 2020 15:49:19 +1000 Subject: [PATCH 01/15] added rds datasource for appsync --- .../@aws-cdk/aws-appsync/lib/data-source.ts | 33 +++++++++++++ .../@aws-cdk/aws-appsync/lib/graphqlapi.ts | 28 ++++++++++- packages/@aws-cdk/aws-appsync/package.json | 6 ++- .../aws-appsync/test/integ.graphql.ts | 47 ++++++++++++++++++- .../@aws-cdk/aws-appsync/test/schema.graphql | 2 + 5 files changed, 112 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/data-source.ts b/packages/@aws-cdk/aws-appsync/lib/data-source.ts index 0daf1f996a452..4646efee3d8b6 100644 --- a/packages/@aws-cdk/aws-appsync/lib/data-source.ts +++ b/packages/@aws-cdk/aws-appsync/lib/data-source.ts @@ -1,6 +1,8 @@ import { ITable } from '@aws-cdk/aws-dynamodb'; import { IGrantable, IPrincipal, IRole, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import { IFunction } from '@aws-cdk/aws-lambda'; +import { IDatabaseCluster } from '@aws-cdk/aws-rds'; +import { ISecret } from '@aws-cdk/aws-secretsmanager'; import { Construct, IResolvable } from '@aws-cdk/core'; import { CfnDataSource } from './appsync.generated'; import { GraphQLApi } from './graphqlapi'; @@ -248,4 +250,35 @@ export class LambdaDataSource extends BackedDataSource { }); props.lambdaFunction.grantInvoke(this); } +} + +/** + * Properties for an AppSync RDS datasource + */ +export interface RdsDataSourceProps extends BackedDataSourceProps { + /** + * The database cluster to call to interact with this data source + */ + readonly databaseCluster: IDatabaseCluster; + + readonly secretStore: ISecret; +} + +/** + * An AppSync datasource backed by RDS + */ +export class RdsDataSource extends BackedDataSource { + constructor(scope: Construct, id: string, props: RdsDataSourceProps) { + super(scope, id, props, { + type: 'RELATIONAL_DATABASE', + relationalDatabaseConfig: { + rdsHttpEndpointConfig: { + awsRegion: props.databaseCluster.stack.region, + dbClusterIdentifier: props.databaseCluster.clusterIdentifier, + awsSecretStoreArn: props.secretStore.secretArn, + }, + relationalDatabaseSourceType: 'RDS_HTTP_ENDPOINT', + }, + }); + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index c01883c81b011..c50561a5124b7 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -1,3 +1,4 @@ +import { readFileSync } from 'fs'; import { IUserPool } from '@aws-cdk/aws-cognito'; import { ITable } from '@aws-cdk/aws-dynamodb'; import { @@ -6,14 +7,15 @@ import { ServicePrincipal, } from '@aws-cdk/aws-iam'; import { IFunction } from '@aws-cdk/aws-lambda'; +import { IDatabaseCluster } from '@aws-cdk/aws-rds'; +import { ISecret } from '@aws-cdk/aws-secretsmanager'; import { Construct, Duration, IResolvable } from '@aws-cdk/core'; -import { readFileSync } from 'fs'; import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema, } from './appsync.generated'; -import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource } from './data-source'; +import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource, RdsDataSource } from './data-source'; /** * enum with all possible values for AppSync authorization type @@ -433,6 +435,28 @@ export class GraphQLApi extends Construct { }); } + /** + * add a new Rds data source to this API + * @param name The name of the data source + * @param description The description of the data source + * @param databaseCluster The database cluster to interact with this data source + * @param secretStore The secret store that contains the username and password for the database cluster + */ + public addRdsDataSource( + name: string, + description: string, + databaseCluster: IDatabaseCluster, + secretStore: ISecret, + ): RdsDataSource { + return new RdsDataSource(this, `${name}DS`, { + api: this, + description, + name, + databaseCluster, + secretStore, + }); + } + private validateAuthorizationProps(props: GraphQLApiProps) { const defaultAuthorizationType = props.authorizationConfig?.defaultAuthorization?.authorizationType || diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index ed431e29e7367..d891547c217ea 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -66,6 +66,7 @@ "@aws-cdk/assert": "0.0.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", + "@aws-cdk/aws-ec2": "0.0.0", "cfn2ts": "0.0.0", "jest": "^25.5.4", "pkglint": "0.0.0" @@ -73,7 +74,9 @@ "dependencies": { "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/aws-dynamodb": "0.0.0", + "@aws-cdk/aws-rds": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-secretsmanager": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.0.2" @@ -85,7 +88,8 @@ "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/core": "0.0.0", - "constructs": "^3.0.2" + "constructs": "^3.0.2", + "@aws-cdk/aws-rds": "0.0.0" }, "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts index 5ced7472fcb05..5c8a5b21a44f6 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts @@ -1,7 +1,9 @@ +import { join } from 'path'; import { UserPool } from '@aws-cdk/aws-cognito'; import { AttributeType, BillingMode, Table } from '@aws-cdk/aws-dynamodb'; +import { Vpc, SecurityGroup, SubnetType, InstanceType, InstanceClass, InstanceSize } from '@aws-cdk/aws-ec2'; +import { DatabaseSecret, CfnDBCluster, CfnDBSubnetGroup, DatabaseCluster, DatabaseClusterEngine, AuroraMysqlEngineVersion } from '@aws-cdk/aws-rds'; import { App, RemovalPolicy, Stack } from '@aws-cdk/core'; -import { join } from 'path'; import { AuthorizationType, GraphQLApi, @@ -219,6 +221,49 @@ httpDS.createResolver({ $utils.appendError($ctx.result.body, "$ctx.result.statusCode") #end `), + + +}); + +const vpc = new Vpc(stack, 'Vpc', {maxAzs: 2}); + +const securityGroup = new SecurityGroup(stack, 'AuroraSecurityGroup', { + vpc, + allowAllOutbound: true, +}); + +const secret = new DatabaseSecret(stack, 'AuroraSecret', { + username: 'clusteradmin', +}); + +const cluster = new DatabaseCluster(stack, 'AuroraCluster', { + engine: DatabaseClusterEngine.auroraMysql({ + version: AuroraMysqlEngineVersion.VER_2_07_1, + }), + masterUser: { + username: 'clusteradmin', + }, + clusterIdentifier: 'db-endpoint-test', + instanceProps: { + instanceType: InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.SMALL), + vpcSubnets: { + subnetType: SubnetType.PRIVATE, + }, + vpc, + securityGroups: [securityGroup], + }, + defaultDatabaseName: 'Animals', +}); + +const rdsDS = api.addRdsDataSource('rds', 'The rds data source', cluster, secret); + +rdsDS.createResolver({ + typeName: 'Query', + fieldName: 'getDatabase', + requestMappingTemplate: MappingTemplate.fromString(` + `), + responseMappingTemplate: MappingTemplate.fromString(` + `), }); app.synth(); diff --git a/packages/@aws-cdk/aws-appsync/test/schema.graphql b/packages/@aws-cdk/aws-appsync/test/schema.graphql index 67576c3ed816c..10f2bd96adc2c 100644 --- a/packages/@aws-cdk/aws-appsync/test/schema.graphql +++ b/packages/@aws-cdk/aws-appsync/test/schema.graphql @@ -37,6 +37,7 @@ type Query @aws_api_key { getCustomerOrdersFilter(customer: String, order: String): Order getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order getPayment(id: String): Payment + get } input FirstOrderInput { @@ -51,4 +52,5 @@ type Mutation @aws_api_key { saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order savePayment(payment: PaymentInput!): Payment doPostOnAws: String! + } From ce78424be8541a50c10323179186f5b8cfefb9a5 Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Wed, 29 Jul 2020 02:31:31 +1000 Subject: [PATCH 02/15] fixed tests for appsync --- .../@aws-cdk/aws-appsync/lib/data-source.ts | 4 +- packages/@aws-cdk/aws-appsync/package.json | 3 +- .../aws-appsync/test/appsync-apikey.test.ts | 2 +- .../@aws-cdk/aws-appsync/test/appsync.test.ts | 2 +- .../test/integ.graphql.expected.json | 601 +++++++++++++++++- .../aws-appsync/test/integ.graphql.ts | 14 +- .../@aws-cdk/aws-appsync/test/schema.graphql | 3 +- 7 files changed, 618 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/data-source.ts b/packages/@aws-cdk/aws-appsync/lib/data-source.ts index 4646efee3d8b6..c3d32a2c1276e 100644 --- a/packages/@aws-cdk/aws-appsync/lib/data-source.ts +++ b/packages/@aws-cdk/aws-appsync/lib/data-source.ts @@ -260,7 +260,9 @@ export interface RdsDataSourceProps extends BackedDataSourceProps { * The database cluster to call to interact with this data source */ readonly databaseCluster: IDatabaseCluster; - + /** + * The secret containing the credentials for the database + */ readonly secretStore: ISecret; } diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index d891547c217ea..3f3f9538b9268 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -89,7 +89,8 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.0.2", - "@aws-cdk/aws-rds": "0.0.0" + "@aws-cdk/aws-rds": "0.0.0", + "@aws-cdk/aws-secretsmanager": "0.0.0" }, "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-apikey.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-apikey.test.ts index 14b87e5eee463..7220de9e1728a 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-apikey.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-apikey.test.ts @@ -1,6 +1,6 @@ import '@aws-cdk/assert/jest'; -import * as cdk from '@aws-cdk/core'; import * as path from 'path'; +import * as cdk from '@aws-cdk/core'; import * as appsync from '../lib'; describe('AppSync Authorization Config', () => { diff --git a/packages/@aws-cdk/aws-appsync/test/appsync.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync.test.ts index edb263eed6800..1d0e01459840b 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync.test.ts @@ -1,6 +1,6 @@ import '@aws-cdk/assert/jest'; -import * as cdk from '@aws-cdk/core'; import * as path from 'path'; +import * as cdk from '@aws-cdk/core'; import * as appsync from '../lib'; test('should not throw an Error', () => { diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json index dc19df3e4c395..29a0593a5e91a 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json @@ -118,7 +118,7 @@ "ApiId" ] }, - "Definition": "type ServiceVersion @aws_api_key {\n version: String!\n}\n\ntype Customer @aws_api_key {\n id: String!\n name: String!\n}\n\ninput SaveCustomerInput {\n name: String!\n}\n\ntype Order @aws_api_key {\n customer: String!\n order: String!\n}\n\ntype Payment @aws_api_key {\n id: String!\n amount: String!\n}\n\ninput PaymentInput {\n amount: String!\n}\n\ntype Query @aws_api_key {\n getServiceVersion: ServiceVersion\n getCustomers: [Customer]\n getCustomer(id: String): Customer\n getCustomerOrdersEq(customer: String): Order\n getCustomerOrdersLt(customer: String): Order\n getCustomerOrdersLe(customer: String): Order\n getCustomerOrdersGt(customer: String): Order\n getCustomerOrdersGe(customer: String): Order\n getCustomerOrdersFilter(customer: String, order: String): Order\n getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order\n getPayment(id: String): Payment\n}\n\ninput FirstOrderInput {\n product: String!\n quantity: Int!\n}\n\ntype Mutation @aws_api_key {\n addCustomer(customer: SaveCustomerInput!): Customer\n saveCustomer(id: String!, customer: SaveCustomerInput!): Customer\n removeCustomer(id: String!): Customer\n saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order\n savePayment(payment: PaymentInput!): Payment\n doPostOnAws: String!\n}\n" + "Definition": "type ServiceVersion @aws_api_key {\n version: String!\n}\n\ntype Customer @aws_api_key {\n id: String!\n name: String!\n}\n\ninput SaveCustomerInput {\n name: String!\n}\n\ntype Order @aws_api_key {\n customer: String!\n order: String!\n}\n\ntype Payment @aws_api_key {\n id: String!\n amount: String!\n}\n\ninput PaymentInput {\n amount: String!\n}\n\ntype Query @aws_api_key {\n getServiceVersion: ServiceVersion\n getCustomers: [Customer]\n getCustomer(id: String): Customer\n getCustomerOrdersEq(customer: String): Order\n getCustomerOrdersLt(customer: String): Order\n getCustomerOrdersLe(customer: String): Order\n getCustomerOrdersGt(customer: String): Order\n getCustomerOrdersGe(customer: String): Order\n getCustomerOrdersFilter(customer: String, order: String): Order\n getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order\n getPayment(id: String): Payment\n getDatabaseVersion: String\n}\n\ninput FirstOrderInput {\n product: String!\n quantity: Int!\n}\n\ntype Mutation @aws_api_key {\n addCustomer(customer: SaveCustomerInput!): Customer\n saveCustomer(id: String!, customer: SaveCustomerInput!): Customer\n removeCustomer(id: String!): Customer\n saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order\n savePayment(payment: PaymentInput!): Payment\n doPostOnAws: String!\n}\n" } }, "ApiNoneDSB4E6495F": { @@ -805,6 +805,78 @@ "ApiSchema510EECD7" ] }, + "ApirdsDSServiceRoleFAB76F9A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appsync.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ApirdsDSF29B2B51": { + "Type": "AWS::AppSync::DataSource", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "ApiF70053CD", + "ApiId" + ] + }, + "Name": "rds", + "Type": "RELATIONAL_DATABASE", + "Description": "The rds data source", + "RelationalDatabaseConfig": { + "RdsHttpEndpointConfig": { + "AwsRegion": { + "Ref": "AWS::Region" + }, + "AwsSecretStoreArn": { + "Ref": "AuroraSecret41E6E877" + }, + "DbClusterIdentifier": { + "Ref": "AuroraCluster23D869C0" + } + }, + "RelationalDatabaseSourceType": "RDS_HTTP_ENDPOINT" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "ApirdsDSServiceRoleFAB76F9A", + "Arn" + ] + } + } + }, + "ApirdsDSQuerygetDatabaseVersionResolverB187F1D8": { + "Type": "AWS::AppSync::Resolver", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "ApiF70053CD", + "ApiId" + ] + }, + "FieldName": "getDatabaseVersion", + "TypeName": "Query", + "DataSourceName": "rds", + "Kind": "UNIT", + "RequestMappingTemplate": "\n {\n \"version\": \"2018-05-29\",\n \"statements\": [\n $util.toJson(\"SHOW VARIABLES LIKE \"%version%\";\")\n ],\n \"variableMap\": {}\n }\n ", + "ResponseMappingTemplate": "$util.toJson($ctx.result)" + }, + "DependsOn": [ + "ApirdsDSF29B2B51", + "ApiSchema510EECD7" + ] + }, "CustomerTable260DCC08": { "Type": "AWS::DynamoDB::Table", "Properties": { @@ -872,6 +944,533 @@ }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" + }, + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.192.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-appsync-integ/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "AuroraSecurityGroup75F699F6": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-appsync-integ/AuroraSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "AuroraSecret41E6E877": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "GenerateSecretString": { + "ExcludeCharacters": "\"@/\\", + "GenerateStringKey": "password", + "PasswordLength": 30, + "SecretStringTemplate": "{\"username\":\"clusteradmin\"}" + } + } + }, + "AuroraClusterSubnetsF3E9E6AD": { + "Type": "AWS::RDS::DBSubnetGroup", + "Properties": { + "DBSubnetGroupDescription": "Subnets for AuroraCluster database", + "SubnetIds": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + } + }, + "AuroraClusterSecret8E4F2BC8": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "GenerateSecretString": { + "ExcludeCharacters": "\"@/\\", + "GenerateStringKey": "password", + "PasswordLength": 30, + "SecretStringTemplate": "{\"username\":\"clusteradmin\"}" + } + } + }, + "AuroraClusterSecretAttachmentDB8032DA": { + "Type": "AWS::SecretsManager::SecretTargetAttachment", + "Properties": { + "SecretId": { + "Ref": "AuroraClusterSecret8E4F2BC8" + }, + "TargetId": { + "Ref": "AuroraCluster23D869C0" + }, + "TargetType": "AWS::RDS::DBCluster" + } + }, + "AuroraCluster23D869C0": { + "Type": "AWS::RDS::DBCluster", + "Properties": { + "Engine": "aurora-mysql", + "DatabaseName": "Animals", + "DBClusterIdentifier": "db-endpoint-test", + "DBClusterParameterGroupName": "default.aurora-mysql5.7", + "DBSubnetGroupName": { + "Ref": "AuroraClusterSubnetsF3E9E6AD" + }, + "EngineVersion": "5.7.mysql_aurora.2.07.1", + "MasterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "AuroraClusterSecret8E4F2BC8" + }, + ":SecretString:username::}}" + ] + ] + }, + "MasterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "AuroraClusterSecret8E4F2BC8" + }, + ":SecretString:password::}}" + ] + ] + }, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "AuroraSecurityGroup75F699F6", + "GroupId" + ] + } + ] + }, + "UpdateReplacePolicy": "Snapshot" + }, + "AuroraClusterInstance19E8278EB": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "DBInstanceClass": "db.t2.small", + "DBClusterIdentifier": { + "Ref": "AuroraCluster23D869C0" + }, + "DBInstanceIdentifier": "db-endpoint-testinstance1", + "DBSubnetGroupName": { + "Ref": "AuroraClusterSubnetsF3E9E6AD" + }, + "Engine": "aurora-mysql", + "EngineVersion": "5.7.mysql_aurora.2.07.1", + "PubliclyAccessible": false + }, + "DependsOn": [ + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet2DefaultRoute060D2087" + ] + }, + "AuroraClusterInstance2FE2217C4": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "DBInstanceClass": "db.t2.small", + "DBClusterIdentifier": { + "Ref": "AuroraCluster23D869C0" + }, + "DBInstanceIdentifier": "db-endpoint-testinstance2", + "DBSubnetGroupName": { + "Ref": "AuroraClusterSubnetsF3E9E6AD" + }, + "Engine": "aurora-mysql", + "EngineVersion": "5.7.mysql_aurora.2.07.1", + "PubliclyAccessible": false + }, + "DependsOn": [ + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet2DefaultRoute060D2087" + ] } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts index 5c8a5b21a44f6..9f61d33fa6767 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts @@ -2,7 +2,7 @@ import { join } from 'path'; import { UserPool } from '@aws-cdk/aws-cognito'; import { AttributeType, BillingMode, Table } from '@aws-cdk/aws-dynamodb'; import { Vpc, SecurityGroup, SubnetType, InstanceType, InstanceClass, InstanceSize } from '@aws-cdk/aws-ec2'; -import { DatabaseSecret, CfnDBCluster, CfnDBSubnetGroup, DatabaseCluster, DatabaseClusterEngine, AuroraMysqlEngineVersion } from '@aws-cdk/aws-rds'; +import { DatabaseSecret, DatabaseCluster, DatabaseClusterEngine, AuroraMysqlEngineVersion } from '@aws-cdk/aws-rds'; import { App, RemovalPolicy, Stack } from '@aws-cdk/core'; import { AuthorizationType, @@ -259,11 +259,17 @@ const rdsDS = api.addRdsDataSource('rds', 'The rds data source', cluster, secret rdsDS.createResolver({ typeName: 'Query', - fieldName: 'getDatabase', + fieldName: 'getDatabaseVersion', requestMappingTemplate: MappingTemplate.fromString(` + { + "version": "2018-05-29", + "statements": [ + $util.toJson("SHOW VARIABLES LIKE "%version%";") + ], + "variableMap": {} + } `), - responseMappingTemplate: MappingTemplate.fromString(` - `), + responseMappingTemplate: MappingTemplate.dynamoDbResultItem(), }); app.synth(); diff --git a/packages/@aws-cdk/aws-appsync/test/schema.graphql b/packages/@aws-cdk/aws-appsync/test/schema.graphql index 10f2bd96adc2c..6d5cf00313d62 100644 --- a/packages/@aws-cdk/aws-appsync/test/schema.graphql +++ b/packages/@aws-cdk/aws-appsync/test/schema.graphql @@ -37,7 +37,7 @@ type Query @aws_api_key { getCustomerOrdersFilter(customer: String, order: String): Order getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order getPayment(id: String): Payment - get + getDatabaseVersion: String } input FirstOrderInput { @@ -52,5 +52,4 @@ type Mutation @aws_api_key { saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order savePayment(payment: PaymentInput!): Payment doPostOnAws: String! - } From 105ccbc09bdf4192317d703e83f9f55825305d04 Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Wed, 29 Jul 2020 12:53:54 +1000 Subject: [PATCH 03/15] added examples in README --- packages/@aws-cdk/aws-appsync/README.md | 74 +++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index f9811ddd9c1be..a706384d132ae 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -29,13 +29,15 @@ type demo { version: String! } type Query { - getDemos: [ test! ] + getDemosDynamo: [ demo! ] + getDemosRds: [ demo! ] } input DemoInput { version: String! } type Mutation { - addDemo(input: DemoInput!): demo + addDemoDynamo(input: DemoInput!): demo + addDemoRds(input: DemoInput!): demo } ``` @@ -44,6 +46,7 @@ CDK stack file `app-stack.ts`: ```ts import * as appsync from '@aws-cdk/aws-appsync'; import * as db from '@aws-cdk/aws-dynamodb'; +import { DatabaseSecret, DatabaseCluster, DatabaseClusterEngine, AuroraMysqlEngineVersion } from '@aws-cdk/aws-rds'; const api = new appsync.GraphQLApi(stack, 'Api', { name: 'demo', @@ -64,21 +67,80 @@ const demoTable = new db.Table(stack, 'DemoTable', { const demoDS = api.addDynamoDbDataSource('demoDataSource', 'Table for Demos"', demoTable); -// Resolver for the Query "getDemos" that scans the DyanmoDb table and returns the entire list. +// Resolver for the Query "getDemosDynamo" that scans the DyanmoDb table and returns the entire list. demoDS.createResolver({ typeName: 'Query', - fieldName: 'getDemos', + fieldName: 'getDemosDynamo', requestMappingTemplate: MappingTemplate.dynamoDbScanTable(), responseMappingTemplate: MappingTemplate.dynamoDbResultList(), }); -// Resolver for the Mutation "addDemo" that puts the item into the DynamoDb table. +// Resolver for the Mutation "addDemoDynamo" that puts the item into the DynamoDb table. demoDS.createResolver({ typeName: 'Mutation', - fieldName: 'addDemo', + fieldName: 'addDemoDynamo', requestMappingTemplate: MappingTemplate.dynamoDbPutItem(PrimaryKey.partition('id').auto(), Values.projecting('demo')), responseMappingTemplate: MappingTemplate.dynamoDbResultItem(), }); + +// Create username and password secret for DB Cluster +const secret = new DatabaseSecret(stack, 'AuroraSecret', { + username: 'clusteradmin', +}); + +// Create the DB cluster, provide all values needed to customise the database. +const cluster = new DatabaseCluster(stack, 'AuroraCluster', { + engine: DatabaseClusterEngine.auroraMysql({ + version: AuroraMysqlEngineVersion.VER_2_07_1, + }), + masterUser: { + username: 'clusteradmin', + }, + clusterIdentifier: 'db-endpoint-test', + defaultDatabaseName: 'demos', +}); + +// Build a data source for AppSync to access the database. +const rdsDS = api.addRdsDataSource('rds', 'The rds data source', cluster, secret); + +// Set up a resolver for an RDS query. +rdsDS.createResolver({ + typeName: 'Query', + fieldName: 'getDemosRds', + requestMappingTemplate: MappingTemplate.fromString(` + { + "version": "2018-05-29", + "statements": [ + "SELECT * FROM demos" + ] + } + `), + responseMappingTemplate: MappingTemplate.fromString(` + $util.rds.toJsonObject($ctx.result) + `), +}); + +// Set up a resolver for an RDS mutation. +rdsDS.createResolver({ + typeName: 'Mutation', + fieldName: 'addDemoRds', + requestMappingTemplate: MappingTemplate.fromString(` + { + "version": "2018-05-29", + "statements": [ + "INSERT INTO demos VALUES (:id, :version)", + "SELECT * WHERE id = :id" + ], + "variableMap": { + ":id": $util.toJson($util.autoId()), + ":version": $util.toJson($ctx.args.version) + } + } + `), + responseMappingTemplate: MappingTemplate.fromString(` + $util.rds.toJsonObject($ctx.result) + `), +}); ``` ## Permissions From 876485e66a39f93ae89a2d0b4a8c1bacf555a9e4 Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Tue, 4 Aug 2020 11:45:34 +1000 Subject: [PATCH 04/15] Update packages/@aws-cdk/aws-appsync/README.md Co-authored-by: Bryan Pan --- packages/@aws-cdk/aws-appsync/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index a706384d132ae..133036552d988 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -46,7 +46,7 @@ CDK stack file `app-stack.ts`: ```ts import * as appsync from '@aws-cdk/aws-appsync'; import * as db from '@aws-cdk/aws-dynamodb'; -import { DatabaseSecret, DatabaseCluster, DatabaseClusterEngine, AuroraMysqlEngineVersion } from '@aws-cdk/aws-rds'; +import * as rds from '@aws-cdk/aws-rds'; const api = new appsync.GraphQLApi(stack, 'Api', { name: 'demo', @@ -220,4 +220,4 @@ api.grantMutation(role, 'updateExample'); // For custom types and granular design api.grant(role, appsync.IamResource.ofType('Mutation', 'updateExample'), 'appsync:GraphQL'); -``` \ No newline at end of file +``` From b972518b752c7c7ed37a1b0b1e5123f959e57b27 Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Tue, 4 Aug 2020 11:45:53 +1000 Subject: [PATCH 05/15] Update packages/@aws-cdk/aws-appsync/README.md Co-authored-by: Bryan Pan --- packages/@aws-cdk/aws-appsync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 133036552d988..79f6dcd1aa921 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -84,7 +84,7 @@ demoDS.createResolver({ }); // Create username and password secret for DB Cluster -const secret = new DatabaseSecret(stack, 'AuroraSecret', { +const secret = new rds.DatabaseSecret(stack, 'AuroraSecret', { username: 'clusteradmin', }); From fa5517723ed9ed6714ce045afb956847fc58dffa Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Tue, 4 Aug 2020 11:46:09 +1000 Subject: [PATCH 06/15] Update packages/@aws-cdk/aws-appsync/README.md Co-authored-by: Bryan Pan --- packages/@aws-cdk/aws-appsync/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 79f6dcd1aa921..070237b9fe603 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -89,9 +89,9 @@ const secret = new rds.DatabaseSecret(stack, 'AuroraSecret', { }); // Create the DB cluster, provide all values needed to customise the database. -const cluster = new DatabaseCluster(stack, 'AuroraCluster', { - engine: DatabaseClusterEngine.auroraMysql({ - version: AuroraMysqlEngineVersion.VER_2_07_1, +const cluster = new rds.DatabaseCluster(stack, 'AuroraCluster', { + engine: rds.DatabaseClusterEngine.auroraMysql({ + version: rds.AuroraMysqlEngineVersion.VER_2_07_1, }), masterUser: { username: 'clusteradmin', From 8c56bcff081f17542d22dbbcbf7020efc6aeafcd Mon Sep 17 00:00:00 2001 From: Bryan Pan Date: Thu, 20 Aug 2020 11:27:34 -0700 Subject: [PATCH 07/15] adjust code-base to reflect master --- .../@aws-cdk/aws-appsync/lib/data-source.ts | 23 +- .../aws-appsync/lib/graphqlapi-base.ts | 41 +++- .../aws-appsync/test/appsync-rds.test.ts | 205 ++++++++++++++++++ .../aws-appsync/test/integ.graphql.ts | 4 +- 4 files changed, 268 insertions(+), 5 deletions(-) create mode 100644 packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts diff --git a/packages/@aws-cdk/aws-appsync/lib/data-source.ts b/packages/@aws-cdk/aws-appsync/lib/data-source.ts index 34a8389f615f4..31776bb89a383 100644 --- a/packages/@aws-cdk/aws-appsync/lib/data-source.ts +++ b/packages/@aws-cdk/aws-appsync/lib/data-source.ts @@ -1,9 +1,9 @@ import { ITable } from '@aws-cdk/aws-dynamodb'; -import { IGrantable, IPrincipal, IRole, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; +import { Grant, IGrantable, IPrincipal, IRole, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import { IFunction } from '@aws-cdk/aws-lambda'; import { IDatabaseCluster } from '@aws-cdk/aws-rds'; import { ISecret } from '@aws-cdk/aws-secretsmanager'; -import { Construct, IResolvable } from '@aws-cdk/core'; +import { Construct, IResolvable, Stack } from '@aws-cdk/core'; import { CfnDataSource } from './appsync.generated'; import { IGraphqlApi } from './graphqlapi-base'; import { BaseResolverProps, Resolver } from './resolver'; @@ -284,5 +284,24 @@ export class RdsDataSource extends BackedDataSource { relationalDatabaseSourceType: 'RDS_HTTP_ENDPOINT', }, }); + props.secretStore.grantRead(this); + const clusterArn = Stack.of(this).formatArn({ + service: 'rds', + resource: `cluster:${props.databaseCluster.clusterIdentifier}`, + }); + // Change to grant with RDS grant becomes implemented + Grant.addToPrincipal({ + grantee: this, + actions: [ + 'rds-data:DeleteItems', + 'rds-data:ExecuteSql', + 'rds-data:ExecuteStatement', + 'rds-data:GetItems', + 'rds-data:InsertItems', + 'rds-data:UpdateItems', + ], + resourceArns: [clusterArn, `${clusterArn}:*`], + scope: this, + }); } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts index 2f357c142db91..a96085e5ccf8c 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts @@ -1,7 +1,9 @@ import { ITable } from '@aws-cdk/aws-dynamodb'; import { IFunction } from '@aws-cdk/aws-lambda'; +import { IDatabaseCluster } from '@aws-cdk/aws-rds'; +import { ISecret } from '@aws-cdk/aws-secretsmanager'; import { CfnResource, IResource, Resource } from '@aws-cdk/core'; -import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource } from './data-source'; +import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource, RdsDataSource } from './data-source'; /** * Optional configuration for data sources @@ -78,6 +80,21 @@ export interface IGraphqlApi extends IResource { */ addLambdaDataSource(id: string, lambdaFunction: IFunction, options?: DataSourceOptions): LambdaDataSource; + /** + * add a new Rds data source to this API + * + * @param id The data source's id + * @param databaseCluster The database cluster to interact with this data source + * @param secretStore The secret store that contains the username and password for the database cluster + * @param options The optional configuration for this data source + */ + addRdsDataSource( + id: string, + databaseCluster: IDatabaseCluster, + secretStore: ISecret, + options?: DataSourceOptions + ): RdsDataSource; + /** * Add schema dependency if not imported * @@ -165,6 +182,28 @@ export abstract class GraphqlApiBase extends Resource implements IGraphqlApi { }); } + /** + * add a new Rds data source to this API + * @param id The data source's id + * @param databaseCluster The database cluster to interact with this data source + * @param secretStore The secret store that contains the username and password for the database cluster + * @param options The optional configuration for this data source + */ + public addRdsDataSource( + id: string, + databaseCluster: IDatabaseCluster, + secretStore: ISecret, + options?: DataSourceOptions, + ): RdsDataSource { + return new RdsDataSource(this, id, { + api: this, + name: options?.name, + description: options?.description, + databaseCluster, + secretStore, + }); + } + /** * Add schema dependency if not imported * diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts new file mode 100644 index 0000000000000..dc2e058741ea5 --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts @@ -0,0 +1,205 @@ +import '@aws-cdk/assert/jest'; +import * as path from 'path'; +import { Vpc, SecurityGroup, SubnetType, InstanceType, InstanceClass, InstanceSize } from '@aws-cdk/aws-ec2'; +import { DatabaseSecret, DatabaseCluster, DatabaseClusterEngine, AuroraMysqlEngineVersion } from '@aws-cdk/aws-rds'; +import * as cdk from '@aws-cdk/core'; +import * as appsync from '../lib'; + +// GLOBAL GIVEN +let stack: cdk.Stack; +let api: appsync.GraphQLApi; +beforeEach(() => { + stack = new cdk.Stack(); + api = new appsync.GraphQLApi(stack, 'baseApi', { + name: 'api', + schemaDefinition: appsync.SchemaDefinition.FILE, + schemaDefinitionFile: path.join(__dirname, 'appsync.test.graphql'), + }); +}); + +describe('Rds Data Source configuration', () => { + // GIVEN + let secret: DatabaseSecret; + let cluster: DatabaseCluster; + beforeEach(() => { + const vpc = new Vpc(stack, 'Vpc', { maxAzs: 2 }); + const securityGroup = new SecurityGroup(stack, 'AuroraSecurityGroup', { + vpc, + allowAllOutbound: true, + }); + secret = new DatabaseSecret(stack, 'AuroraSecret', { + username: 'clusteradmin', + }); + cluster = new DatabaseCluster(stack, 'AuroraCluster', { + engine: DatabaseClusterEngine.auroraMysql({ version: AuroraMysqlEngineVersion.VER_2_07_1 }), + masterUser: { username: 'clusteradmin' }, + clusterIdentifier: 'db-endpoint-test', + instanceProps: { + instanceType: InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.SMALL), + vpcSubnets: { subnetType: SubnetType.PRIVATE }, + vpc, + securityGroups: [securityGroup], + }, + defaultDatabaseName: 'Animals', + }); + }); + + test('appsync creates correct policy', () => { + // WHEN + api.addRdsDataSource('ds', cluster, secret); + + // THEN + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [{ + Action: [ + 'secretsmanager:GetSecretValue', + 'secretsmanager:DescribeSecret', + ], + Effect: 'Allow', + Resource: { Ref: 'AuroraSecret41E6E877' }, + }, + { + Action: [ + 'rds-data:DeleteItems', + 'rds-data:ExecuteSql', + 'rds-data:ExecuteStatement', + 'rds-data:GetItems', + 'rds-data:InsertItems', + 'rds-data:UpdateItems', + ], + Effect: 'Allow', + Resource: [{ + 'Fn::Join': ['', ['arn:', + { Ref: 'AWS::Partition' }, + ':rds:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':cluster:', + { Ref: 'AuroraCluster23D869C0' }]], + }, + { + 'Fn::Join': ['', ['arn:', + { Ref: 'AWS::Partition' }, + ':rds:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':cluster:', + { Ref: 'AuroraCluster23D869C0' }, + ':*']], + }], + }], + }, + }); + }); + + test('default configuration produces name identical to the id', () => { + // WHEN + api.addRdsDataSource('ds', cluster, secret); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'RELATIONAL_DATABASE', + Name: 'ds', + }); + }); + + test('appsync configures name correctly', () => { + // WHEN + api.addRdsDataSource('ds', cluster, secret, { + name: 'custom', + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'RELATIONAL_DATABASE', + Name: 'custom', + }); + }); + + test('appsync configures name and description correctly', () => { + // WHEN + api.addRdsDataSource('ds', cluster, secret, { + name: 'custom', + description: 'custom description', + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'RELATIONAL_DATABASE', + Name: 'custom', + Description: 'custom description', + }); + }); + + test('appsync errors when creating multiple rds data sources with no configuration', () => { + // WHEN + const when = () => { + api.addRdsDataSource('ds', cluster, secret); + api.addRdsDataSource('ds', cluster, secret); + }; + + // THEN + expect(when).toThrow('There is already a Construct with name \'ds\' in GraphQLApi [baseApi]'); + }); +}); + +describe('adding rds data source from imported api', () => { + // GIVEN + let secret: DatabaseSecret; + let cluster: DatabaseCluster; + beforeEach(() => { + const vpc = new Vpc(stack, 'Vpc', { maxAzs: 2 }); + const securityGroup = new SecurityGroup(stack, 'AuroraSecurityGroup', { + vpc, + allowAllOutbound: true, + }); + secret = new DatabaseSecret(stack, 'AuroraSecret', { + username: 'clusteradmin', + }); + cluster = new DatabaseCluster(stack, 'AuroraCluster', { + engine: DatabaseClusterEngine.auroraMysql({ version: AuroraMysqlEngineVersion.VER_2_07_1 }), + masterUser: { username: 'clusteradmin' }, + clusterIdentifier: 'db-endpoint-test', + instanceProps: { + instanceType: InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.SMALL), + vpcSubnets: { subnetType: SubnetType.PRIVATE }, + vpc, + securityGroups: [securityGroup], + }, + defaultDatabaseName: 'Animals', + }); + }); + + test('imported api can add RdsDbDataSource from id', () => { + // WHEN + const importedApi = appsync.GraphQLApi.fromGraphqlApiAttributes(stack, 'importedApi', { + graphqlApiId: api.apiId, + }); + importedApi.addRdsDataSource('ds', cluster, secret); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'RELATIONAL_DATABASE', + ApiId: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'ApiId'] }, + }); + }); + + test('imported api can add RdsDataSource from attributes', () => { + // WHEN + const importedApi = appsync.GraphQLApi.fromGraphqlApiAttributes(stack, 'importedApi', { + graphqlApiId: api.apiId, + graphqlApiArn: api.arn, + }); + importedApi.addRdsDataSource('ds', cluster, secret); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'RELATIONAL_DATABASE', + ApiId: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'ApiId'] }, + }); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts index 9287c8a0e655b..30e9c0652d686 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts @@ -227,7 +227,7 @@ httpDS.createResolver({ }); -const vpc = new Vpc(stack, 'Vpc', {maxAzs: 2}); +const vpc = new Vpc(stack, 'Vpc', { maxAzs: 2 }); const securityGroup = new SecurityGroup(stack, 'AuroraSecurityGroup', { vpc, @@ -257,7 +257,7 @@ const cluster = new DatabaseCluster(stack, 'AuroraCluster', { defaultDatabaseName: 'Animals', }); -const rdsDS = api.addRdsDataSource('rds', 'The rds data source', cluster, secret); +const rdsDS = api.addRdsDataSource('rds', cluster, secret, { description: 'The rds data source' }); rdsDS.createResolver({ typeName: 'Query', From 5d06daf6e46a39129733f505dca403787e7a5e27 Mon Sep 17 00:00:00 2001 From: Bryan Pan Date: Thu, 20 Aug 2020 11:37:28 -0700 Subject: [PATCH 08/15] remove integ test because unncessary --- packages/@aws-cdk/aws-appsync/README.md | 19 +- packages/@aws-cdk/aws-appsync/package.json | 3 +- .../test/integ.graphql.expected.json | 687 +----------------- .../aws-appsync/test/integ.graphql.graphql | 1 - .../aws-appsync/test/integ.graphql.ts | 51 -- 5 files changed, 13 insertions(+), 748 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 04444b9c39678..34345debe54ed 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -29,15 +29,13 @@ type demo { version: String! } type Query { - getDemosDynamo: [ demo! ] - getDemosRds: [ demo! ] + getDemos: [ demo! ] } input DemoInput { version: String! } type Mutation { - addDemoDynamo(input: DemoInput!): demo - addDemoRds(input: DemoInput!): demo + addDemo(input: DemoInput!): demo } ``` @@ -46,7 +44,6 @@ CDK stack file `app-stack.ts`: ```ts import * as appsync from '@aws-cdk/aws-appsync'; import * as db from '@aws-cdk/aws-dynamodb'; -import * as rds from '@aws-cdk/aws-rds'; const api = new appsync.GraphQLApi(stack, 'Api', { name: 'demo', @@ -69,22 +66,26 @@ const demoTable = new db.Table(stack, 'DemoTable', { const demoDS = api.addDynamoDbDataSource('demoDataSource', demoTable); -// Resolver for the Query "getDemosDynamo" that scans the DyanmoDb table and returns the entire list. +// Resolver for the Query "getDemos" that scans the DyanmoDb table and returns the entire list. demoDS.createResolver({ typeName: 'Query', - fieldName: 'getDemosDynamo', + fieldName: 'getDemos', requestMappingTemplate: MappingTemplate.dynamoDbScanTable(), responseMappingTemplate: MappingTemplate.dynamoDbResultList(), }); -// Resolver for the Mutation "addDemoDynamo" that puts the item into the DynamoDb table. +// Resolver for the Mutation "addDemo" that puts the item into the DynamoDb table. demoDS.createResolver({ typeName: 'Mutation', - fieldName: 'addDemoDynamo', + fieldName: 'addDemo', requestMappingTemplate: MappingTemplate.dynamoDbPutItem(PrimaryKey.partition('id').auto(), Values.projecting('demo')), responseMappingTemplate: MappingTemplate.dynamoDbResultItem(), }); +``` + +## Aurora Serverless +```ts // Create username and password secret for DB Cluster const secret = new rds.DatabaseSecret(stack, 'AuroraSecret', { username: 'clusteradmin', diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index 4134d279a4185..4676a7dc4c0e8 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -66,7 +66,6 @@ "@aws-cdk/assert": "0.0.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", - "@aws-cdk/aws-ec2": "0.0.0", "cfn2ts": "0.0.0", "jest": "^25.5.4", "pkglint": "0.0.0" @@ -74,6 +73,7 @@ "dependencies": { "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/aws-dynamodb": "0.0.0", + "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/aws-rds": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-secretsmanager": "0.0.0", @@ -86,6 +86,7 @@ "peerDependencies": { "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/aws-dynamodb": "0.0.0", + "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json index 90d29c7f54a3b..e5eed82bd5992 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json @@ -60,7 +60,7 @@ "ApiId" ] }, - "Definition": "type ServiceVersion @aws_api_key {\n version: String!\n}\n\ntype Customer @aws_api_key {\n id: String!\n name: String!\n}\n\ninput SaveCustomerInput {\n name: String!\n}\n\ntype Order @aws_api_key {\n customer: String!\n order: String!\n}\n\ntype Payment @aws_api_key {\n id: String!\n amount: String!\n}\n\ninput PaymentInput {\n amount: String!\n}\n\ntype Query @aws_api_key {\n getServiceVersion: ServiceVersion\n getCustomers: [Customer]\n getCustomer(id: String): Customer\n getCustomerOrdersEq(customer: String): Order\n getCustomerOrdersLt(customer: String): Order\n getCustomerOrdersLe(customer: String): Order\n getCustomerOrdersGt(customer: String): Order\n getCustomerOrdersGe(customer: String): Order\n getCustomerOrdersFilter(customer: String, order: String): Order\n getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order\n getPayment(id: String): Payment\n getDatabaseVersion: String\n}\n\ninput FirstOrderInput {\n product: String!\n quantity: Int!\n}\n\ntype Mutation @aws_api_key {\n addCustomer(customer: SaveCustomerInput!): Customer\n saveCustomer(id: String!, customer: SaveCustomerInput!): Customer\n removeCustomer(id: String!): Customer\n saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order\n savePayment(payment: PaymentInput!): Payment\n doPostOnAws: String!\n}\n" + "Definition": "type ServiceVersion @aws_api_key {\n version: String!\n}\n\ntype Customer @aws_api_key {\n id: String!\n name: String!\n}\n\ninput SaveCustomerInput {\n name: String!\n}\n\ntype Order @aws_api_key {\n customer: String!\n order: String!\n}\n\ntype Payment @aws_api_key {\n id: String!\n amount: String!\n}\n\ninput PaymentInput {\n amount: String!\n}\n\ntype Query @aws_api_key {\n getServiceVersion: ServiceVersion\n getCustomers: [Customer]\n getCustomer(id: String): Customer\n getCustomerOrdersEq(customer: String): Order\n getCustomerOrdersLt(customer: String): Order\n getCustomerOrdersLe(customer: String): Order\n getCustomerOrdersGt(customer: String): Order\n getCustomerOrdersGe(customer: String): Order\n getCustomerOrdersFilter(customer: String, order: String): Order\n getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order\n getPayment(id: String): Payment\n}\n\ninput FirstOrderInput {\n product: String!\n quantity: Int!\n}\n\ntype Mutation @aws_api_key {\n addCustomer(customer: SaveCustomerInput!): Customer\n saveCustomer(id: String!, customer: SaveCustomerInput!): Customer\n removeCustomer(id: String!): Customer\n saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order\n savePayment(payment: PaymentInput!): Payment\n doPostOnAws: String!\n}\n" } }, "ApiDefaultApiKeyF991C37B": { @@ -756,164 +756,6 @@ "ApiSchema510EECD7" ] }, - "ApirdsServiceRoleB16907AA": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "appsync.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } - } - }, - "ApirdsServiceRoleDefaultPolicy5FBAF8A4": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], - "Effect": "Allow", - "Resource": { - "Ref": "AuroraSecret41E6E877" - } - }, - { - "Action": [ - "rds-data:DeleteItems", - "rds-data:ExecuteSql", - "rds-data:ExecuteStatement", - "rds-data:GetItems", - "rds-data:InsertItems", - "rds-data:UpdateItems" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":rds:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":cluster:", - { - "Ref": "AuroraCluster23D869C0" - } - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":rds:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":cluster:", - { - "Ref": "AuroraCluster23D869C0" - }, - ":*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "ApirdsServiceRoleDefaultPolicy5FBAF8A4", - "Roles": [ - { - "Ref": "ApirdsServiceRoleB16907AA" - } - ] - } - }, - "ApirdsE12CC594": { - "Type": "AWS::AppSync::DataSource", - "Properties": { - "ApiId": { - "Fn::GetAtt": [ - "ApiF70053CD", - "ApiId" - ] - }, - "Name": "rds", - "Type": "RELATIONAL_DATABASE", - "Description": "The rds data source", - "RelationalDatabaseConfig": { - "RdsHttpEndpointConfig": { - "AwsRegion": { - "Ref": "AWS::Region" - }, - "AwsSecretStoreArn": { - "Ref": "AuroraSecret41E6E877" - }, - "DbClusterIdentifier": { - "Ref": "AuroraCluster23D869C0" - } - }, - "RelationalDatabaseSourceType": "RDS_HTTP_ENDPOINT" - }, - "ServiceRoleArn": { - "Fn::GetAtt": [ - "ApirdsServiceRoleB16907AA", - "Arn" - ] - } - } - }, - "ApirdsQuerygetDatabaseVersionResolverB267F9CC": { - "Type": "AWS::AppSync::Resolver", - "Properties": { - "ApiId": { - "Fn::GetAtt": [ - "ApiF70053CD", - "ApiId" - ] - }, - "FieldName": "getDatabaseVersion", - "TypeName": "Query", - "DataSourceName": "rds", - "Kind": "UNIT", - "RequestMappingTemplate": "\n {\n \"version\": \"2018-05-29\",\n \"statements\": [\n $util.toJson(\"SHOW VARIABLES LIKE \"%version%\";\")\n ],\n \"variableMap\": {}\n }\n ", - "ResponseMappingTemplate": "$util.toJson($ctx.result)" - }, - "DependsOn": [ - "ApirdsE12CC594", - "ApiSchema510EECD7" - ] - }, "CustomerTable260DCC08": { "Type": "AWS::DynamoDB::Table", "Properties": { @@ -981,533 +823,6 @@ }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" - }, - "Vpc8378EB38": { - "Type": "AWS::EC2::VPC", - "Properties": { - "CidrBlock": "10.0.0.0/16", - "EnableDnsHostnames": true, - "EnableDnsSupport": true, - "InstanceTenancy": "default", - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc" - } - ] - } - }, - "VpcPublicSubnet1Subnet5C2D37C4": { - "Type": "AWS::EC2::Subnet", - "Properties": { - "CidrBlock": "10.0.0.0/18", - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "AvailabilityZone": "test-region-1a", - "MapPublicIpOnLaunch": true, - "Tags": [ - { - "Key": "aws-cdk:subnet-name", - "Value": "Public" - }, - { - "Key": "aws-cdk:subnet-type", - "Value": "Public" - }, - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PublicSubnet1" - } - ] - } - }, - "VpcPublicSubnet1RouteTable6C95E38E": { - "Type": "AWS::EC2::RouteTable", - "Properties": { - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PublicSubnet1" - } - ] - } - }, - "VpcPublicSubnet1RouteTableAssociation97140677": { - "Type": "AWS::EC2::SubnetRouteTableAssociation", - "Properties": { - "RouteTableId": { - "Ref": "VpcPublicSubnet1RouteTable6C95E38E" - }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - } - } - }, - "VpcPublicSubnet1DefaultRoute3DA9E72A": { - "Type": "AWS::EC2::Route", - "Properties": { - "RouteTableId": { - "Ref": "VpcPublicSubnet1RouteTable6C95E38E" - }, - "DestinationCidrBlock": "0.0.0.0/0", - "GatewayId": { - "Ref": "VpcIGWD7BA715C" - } - }, - "DependsOn": [ - "VpcVPCGWBF912B6E" - ] - }, - "VpcPublicSubnet1EIPD7E02669": { - "Type": "AWS::EC2::EIP", - "Properties": { - "Domain": "vpc", - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PublicSubnet1" - } - ] - } - }, - "VpcPublicSubnet1NATGateway4D7517AA": { - "Type": "AWS::EC2::NatGateway", - "Properties": { - "AllocationId": { - "Fn::GetAtt": [ - "VpcPublicSubnet1EIPD7E02669", - "AllocationId" - ] - }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PublicSubnet1" - } - ] - } - }, - "VpcPublicSubnet2Subnet691E08A3": { - "Type": "AWS::EC2::Subnet", - "Properties": { - "CidrBlock": "10.0.64.0/18", - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "AvailabilityZone": "test-region-1b", - "MapPublicIpOnLaunch": true, - "Tags": [ - { - "Key": "aws-cdk:subnet-name", - "Value": "Public" - }, - { - "Key": "aws-cdk:subnet-type", - "Value": "Public" - }, - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PublicSubnet2" - } - ] - } - }, - "VpcPublicSubnet2RouteTable94F7E489": { - "Type": "AWS::EC2::RouteTable", - "Properties": { - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PublicSubnet2" - } - ] - } - }, - "VpcPublicSubnet2RouteTableAssociationDD5762D8": { - "Type": "AWS::EC2::SubnetRouteTableAssociation", - "Properties": { - "RouteTableId": { - "Ref": "VpcPublicSubnet2RouteTable94F7E489" - }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - } - } - }, - "VpcPublicSubnet2DefaultRoute97F91067": { - "Type": "AWS::EC2::Route", - "Properties": { - "RouteTableId": { - "Ref": "VpcPublicSubnet2RouteTable94F7E489" - }, - "DestinationCidrBlock": "0.0.0.0/0", - "GatewayId": { - "Ref": "VpcIGWD7BA715C" - } - }, - "DependsOn": [ - "VpcVPCGWBF912B6E" - ] - }, - "VpcPublicSubnet2EIP3C605A87": { - "Type": "AWS::EC2::EIP", - "Properties": { - "Domain": "vpc", - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PublicSubnet2" - } - ] - } - }, - "VpcPublicSubnet2NATGateway9182C01D": { - "Type": "AWS::EC2::NatGateway", - "Properties": { - "AllocationId": { - "Fn::GetAtt": [ - "VpcPublicSubnet2EIP3C605A87", - "AllocationId" - ] - }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PublicSubnet2" - } - ] - } - }, - "VpcPrivateSubnet1Subnet536B997A": { - "Type": "AWS::EC2::Subnet", - "Properties": { - "CidrBlock": "10.0.128.0/18", - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "AvailabilityZone": "test-region-1a", - "MapPublicIpOnLaunch": false, - "Tags": [ - { - "Key": "aws-cdk:subnet-name", - "Value": "Private" - }, - { - "Key": "aws-cdk:subnet-type", - "Value": "Private" - }, - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PrivateSubnet1" - } - ] - } - }, - "VpcPrivateSubnet1RouteTableB2C5B500": { - "Type": "AWS::EC2::RouteTable", - "Properties": { - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PrivateSubnet1" - } - ] - } - }, - "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { - "Type": "AWS::EC2::SubnetRouteTableAssociation", - "Properties": { - "RouteTableId": { - "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" - }, - "SubnetId": { - "Ref": "VpcPrivateSubnet1Subnet536B997A" - } - } - }, - "VpcPrivateSubnet1DefaultRouteBE02A9ED": { - "Type": "AWS::EC2::Route", - "Properties": { - "RouteTableId": { - "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" - }, - "DestinationCidrBlock": "0.0.0.0/0", - "NatGatewayId": { - "Ref": "VpcPublicSubnet1NATGateway4D7517AA" - } - } - }, - "VpcPrivateSubnet2Subnet3788AAA1": { - "Type": "AWS::EC2::Subnet", - "Properties": { - "CidrBlock": "10.0.192.0/18", - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "AvailabilityZone": "test-region-1b", - "MapPublicIpOnLaunch": false, - "Tags": [ - { - "Key": "aws-cdk:subnet-name", - "Value": "Private" - }, - { - "Key": "aws-cdk:subnet-type", - "Value": "Private" - }, - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PrivateSubnet2" - } - ] - } - }, - "VpcPrivateSubnet2RouteTableA678073B": { - "Type": "AWS::EC2::RouteTable", - "Properties": { - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc/PrivateSubnet2" - } - ] - } - }, - "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { - "Type": "AWS::EC2::SubnetRouteTableAssociation", - "Properties": { - "RouteTableId": { - "Ref": "VpcPrivateSubnet2RouteTableA678073B" - }, - "SubnetId": { - "Ref": "VpcPrivateSubnet2Subnet3788AAA1" - } - } - }, - "VpcPrivateSubnet2DefaultRoute060D2087": { - "Type": "AWS::EC2::Route", - "Properties": { - "RouteTableId": { - "Ref": "VpcPrivateSubnet2RouteTableA678073B" - }, - "DestinationCidrBlock": "0.0.0.0/0", - "NatGatewayId": { - "Ref": "VpcPublicSubnet2NATGateway9182C01D" - } - } - }, - "VpcIGWD7BA715C": { - "Type": "AWS::EC2::InternetGateway", - "Properties": { - "Tags": [ - { - "Key": "Name", - "Value": "aws-appsync-integ/Vpc" - } - ] - } - }, - "VpcVPCGWBF912B6E": { - "Type": "AWS::EC2::VPCGatewayAttachment", - "Properties": { - "VpcId": { - "Ref": "Vpc8378EB38" - }, - "InternetGatewayId": { - "Ref": "VpcIGWD7BA715C" - } - } - }, - "AuroraSecurityGroup75F699F6": { - "Type": "AWS::EC2::SecurityGroup", - "Properties": { - "GroupDescription": "aws-appsync-integ/AuroraSecurityGroup", - "SecurityGroupEgress": [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - "VpcId": { - "Ref": "Vpc8378EB38" - } - } - }, - "AuroraSecret41E6E877": { - "Type": "AWS::SecretsManager::Secret", - "Properties": { - "Description": { - "Fn::Join": [ - "", - [ - "Generated by the CDK for stack: ", - { - "Ref": "AWS::StackName" - } - ] - ] - }, - "GenerateSecretString": { - "ExcludeCharacters": "\"@/\\", - "GenerateStringKey": "password", - "PasswordLength": 30, - "SecretStringTemplate": "{\"username\":\"clusteradmin\"}" - } - } - }, - "AuroraClusterSubnetsF3E9E6AD": { - "Type": "AWS::RDS::DBSubnetGroup", - "Properties": { - "DBSubnetGroupDescription": "Subnets for AuroraCluster database", - "SubnetIds": [ - { - "Ref": "VpcPrivateSubnet1Subnet536B997A" - }, - { - "Ref": "VpcPrivateSubnet2Subnet3788AAA1" - } - ] - } - }, - "AuroraClusterSecret8E4F2BC8": { - "Type": "AWS::SecretsManager::Secret", - "Properties": { - "Description": { - "Fn::Join": [ - "", - [ - "Generated by the CDK for stack: ", - { - "Ref": "AWS::StackName" - } - ] - ] - }, - "GenerateSecretString": { - "ExcludeCharacters": "\"@/\\", - "GenerateStringKey": "password", - "PasswordLength": 30, - "SecretStringTemplate": "{\"username\":\"clusteradmin\"}" - } - } - }, - "AuroraClusterSecretAttachmentDB8032DA": { - "Type": "AWS::SecretsManager::SecretTargetAttachment", - "Properties": { - "SecretId": { - "Ref": "AuroraClusterSecret8E4F2BC8" - }, - "TargetId": { - "Ref": "AuroraCluster23D869C0" - }, - "TargetType": "AWS::RDS::DBCluster" - } - }, - "AuroraCluster23D869C0": { - "Type": "AWS::RDS::DBCluster", - "Properties": { - "Engine": "aurora-mysql", - "DatabaseName": "Animals", - "DBClusterIdentifier": "db-endpoint-test", - "DBClusterParameterGroupName": "default.aurora-mysql5.7", - "DBSubnetGroupName": { - "Ref": "AuroraClusterSubnetsF3E9E6AD" - }, - "EngineVersion": "5.7.mysql_aurora.2.07.1", - "MasterUsername": { - "Fn::Join": [ - "", - [ - "{{resolve:secretsmanager:", - { - "Ref": "AuroraClusterSecret8E4F2BC8" - }, - ":SecretString:username::}}" - ] - ] - }, - "MasterUserPassword": { - "Fn::Join": [ - "", - [ - "{{resolve:secretsmanager:", - { - "Ref": "AuroraClusterSecret8E4F2BC8" - }, - ":SecretString:password::}}" - ] - ] - }, - "VpcSecurityGroupIds": [ - { - "Fn::GetAtt": [ - "AuroraSecurityGroup75F699F6", - "GroupId" - ] - } - ] - }, - "UpdateReplacePolicy": "Snapshot" - }, - "AuroraClusterInstance19E8278EB": { - "Type": "AWS::RDS::DBInstance", - "Properties": { - "DBInstanceClass": "db.t2.small", - "DBClusterIdentifier": { - "Ref": "AuroraCluster23D869C0" - }, - "DBInstanceIdentifier": "db-endpoint-testinstance1", - "DBSubnetGroupName": { - "Ref": "AuroraClusterSubnetsF3E9E6AD" - }, - "Engine": "aurora-mysql", - "EngineVersion": "5.7.mysql_aurora.2.07.1", - "PubliclyAccessible": false - }, - "DependsOn": [ - "VpcPrivateSubnet1DefaultRouteBE02A9ED", - "VpcPrivateSubnet2DefaultRoute060D2087" - ] - }, - "AuroraClusterInstance2FE2217C4": { - "Type": "AWS::RDS::DBInstance", - "Properties": { - "DBInstanceClass": "db.t2.small", - "DBClusterIdentifier": { - "Ref": "AuroraCluster23D869C0" - }, - "DBInstanceIdentifier": "db-endpoint-testinstance2", - "DBSubnetGroupName": { - "Ref": "AuroraClusterSubnetsF3E9E6AD" - }, - "Engine": "aurora-mysql", - "EngineVersion": "5.7.mysql_aurora.2.07.1", - "PubliclyAccessible": false - }, - "DependsOn": [ - "VpcPrivateSubnet1DefaultRouteBE02A9ED", - "VpcPrivateSubnet2DefaultRoute060D2087" - ] } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.graphql b/packages/@aws-cdk/aws-appsync/test/integ.graphql.graphql index 6d5cf00313d62..67576c3ed816c 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.graphql +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.graphql @@ -37,7 +37,6 @@ type Query @aws_api_key { getCustomerOrdersFilter(customer: String, order: String): Order getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order getPayment(id: String): Payment - getDatabaseVersion: String } input FirstOrderInput { diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts index 30e9c0652d686..607a495d9b735 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts @@ -1,8 +1,6 @@ import { join } from 'path'; import { UserPool } from '@aws-cdk/aws-cognito'; import { AttributeType, BillingMode, Table } from '@aws-cdk/aws-dynamodb'; -import { Vpc, SecurityGroup, SubnetType, InstanceType, InstanceClass, InstanceSize } from '@aws-cdk/aws-ec2'; -import { DatabaseSecret, DatabaseCluster, DatabaseClusterEngine, AuroraMysqlEngineVersion } from '@aws-cdk/aws-rds'; import { App, RemovalPolicy, Stack } from '@aws-cdk/core'; import { AuthorizationType, @@ -223,55 +221,6 @@ httpDS.createResolver({ $utils.appendError($ctx.result.body, "$ctx.result.statusCode") #end `), - - -}); - -const vpc = new Vpc(stack, 'Vpc', { maxAzs: 2 }); - -const securityGroup = new SecurityGroup(stack, 'AuroraSecurityGroup', { - vpc, - allowAllOutbound: true, -}); - -const secret = new DatabaseSecret(stack, 'AuroraSecret', { - username: 'clusteradmin', -}); - -const cluster = new DatabaseCluster(stack, 'AuroraCluster', { - engine: DatabaseClusterEngine.auroraMysql({ - version: AuroraMysqlEngineVersion.VER_2_07_1, - }), - masterUser: { - username: 'clusteradmin', - }, - clusterIdentifier: 'db-endpoint-test', - instanceProps: { - instanceType: InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.SMALL), - vpcSubnets: { - subnetType: SubnetType.PRIVATE, - }, - vpc, - securityGroups: [securityGroup], - }, - defaultDatabaseName: 'Animals', -}); - -const rdsDS = api.addRdsDataSource('rds', cluster, secret, { description: 'The rds data source' }); - -rdsDS.createResolver({ - typeName: 'Query', - fieldName: 'getDatabaseVersion', - requestMappingTemplate: MappingTemplate.fromString(` - { - "version": "2018-05-29", - "statements": [ - $util.toJson("SHOW VARIABLES LIKE "%version%";") - ], - "variableMap": {} - } - `), - responseMappingTemplate: MappingTemplate.dynamoDbResultItem(), }); app.synth(); From 0e26fa2f6485e0d3ebc0dc6f96d5e5e8065343a9 Mon Sep 17 00:00:00 2001 From: Bryan Pan Date: Thu, 20 Aug 2020 12:01:38 -0700 Subject: [PATCH 09/15] update readme --- packages/@aws-cdk/aws-appsync/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 34345debe54ed..6d9a85fc599d5 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -85,6 +85,10 @@ demoDS.createResolver({ ## Aurora Serverless +AppSync provides a data source for executing SQL commands against Amazon Aurora +Serverless clusters. You can use AppSync resolvers to execute SQL statements +against the Data API with GraphQL queries, mutations, and subscriptions. + ```ts // Create username and password secret for DB Cluster const secret = new rds.DatabaseSecret(stack, 'AuroraSecret', { @@ -93,12 +97,8 @@ const secret = new rds.DatabaseSecret(stack, 'AuroraSecret', { // Create the DB cluster, provide all values needed to customise the database. const cluster = new rds.DatabaseCluster(stack, 'AuroraCluster', { - engine: rds.DatabaseClusterEngine.auroraMysql({ - version: rds.AuroraMysqlEngineVersion.VER_2_07_1, - }), - masterUser: { - username: 'clusteradmin', - }, + engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_2_07_1 }), + masterUser: { username: 'clusteradmin' }, clusterIdentifier: 'db-endpoint-test', defaultDatabaseName: 'demos', }); From 40e2c1c5a3ec8dbbcd27787d9c11b96361ee6ea5 Mon Sep 17 00:00:00 2001 From: Bryan Pan Date: Tue, 25 Aug 2020 09:36:30 -0700 Subject: [PATCH 10/15] gosh darnit --- packages/@aws-cdk/aws-appsync/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index 4e7db7a05e69b..dd95b23a65f4b 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -93,7 +93,7 @@ "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/core": "0.0.0", "@aws-cdk/aws-rds": "0.0.0", - "@aws-cdk/aws-secretsmanager": "0.0.0" + "@aws-cdk/aws-secretsmanager": "0.0.0", "constructs": "^3.0.4" }, "engines": { From a2a6d3a204b98be77527ed0c5feb72f60f7e9b81 Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Sun, 18 Oct 2020 10:09:11 +1100 Subject: [PATCH 11/15] fixed README conflict --- packages/@aws-cdk/aws-appsync/README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index b165c86e0b36c..381ae9dd3f95e 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -84,7 +84,6 @@ demoDS.createResolver({ }); ``` -<<<<<<< HEAD ## Aurora Serverless AppSync provides a data source for executing SQL commands against Amazon Aurora @@ -149,7 +148,6 @@ rdsDS.createResolver({ ``` ## Imports -======= #### HTTP Endpoints GraphQL schema file `schema.graphql`: @@ -272,7 +270,6 @@ const api = appsync.GraphqlApi(stack, 'api', { schema: appsync.Schema.fromAsset(join(__dirname, 'schema.graphl')), }); ``` ->>>>>>> 0bb133e8b500be3802bdfeb9c35ef61ad4687223 ### Imports Any GraphQL Api that has been created outside the stack can be imported from @@ -714,7 +711,6 @@ You can create Object Types in three ways: ``` > This method allows for reusability and modularity, ideal for reducing code duplication. -<<<<<<< HEAD 3. Object Types can be created ***internally*** within the GraphQL API. ```ts const api = new appsync.GraphQLApi(stack, 'Api', { @@ -729,7 +725,6 @@ You can create Object Types in three ways: }); ``` > This method provides easy use and is ideal for smaller projects. -======= To learn more about **Object Types**, read the docs [here](https://graphql.org/learn/schema/#object-types-and-fields). ### Enum Types @@ -884,4 +879,3 @@ api.addSubscription('addedFilm', new appsync.ResolvableField({ ``` To learn more about top level operations, check out the docs [here](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-data.html). ->>>>>>> 0bb133e8b500be3802bdfeb9c35ef61ad4687223 From 656db0e6973b3b3656e60336afabd0b9a32d73af Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Mon, 19 Oct 2020 13:20:33 +1100 Subject: [PATCH 12/15] fixed build issues --- .../@aws-cdk/aws-appsync/lib/data-source.ts | 4 ++-- .../aws-appsync/test/appsync-rds.test.ts | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/data-source.ts b/packages/@aws-cdk/aws-appsync/lib/data-source.ts index 16aca621eb328..e13136090b56c 100644 --- a/packages/@aws-cdk/aws-appsync/lib/data-source.ts +++ b/packages/@aws-cdk/aws-appsync/lib/data-source.ts @@ -3,8 +3,8 @@ import { Grant, IGrantable, IPrincipal, IRole, Role, ServicePrincipal } from '@a import { IFunction } from '@aws-cdk/aws-lambda'; import { IDatabaseCluster } from '@aws-cdk/aws-rds'; import { ISecret } from '@aws-cdk/aws-secretsmanager'; -import { Construct, IResolvable, Stack } from '@aws-cdk/core'; -import { IResolvable } from '@aws-cdk/core'; +import { IResolvable, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; import { CfnDataSource } from './appsync.generated'; import { IGraphqlApi } from './graphqlapi-base'; import { BaseResolverProps, Resolver } from './resolver'; diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts index dc2e058741ea5..ac2f4c7f93ef0 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts @@ -7,13 +7,14 @@ import * as appsync from '../lib'; // GLOBAL GIVEN let stack: cdk.Stack; -let api: appsync.GraphQLApi; +let api: appsync.GraphqlApi; beforeEach(() => { stack = new cdk.Stack(); - api = new appsync.GraphQLApi(stack, 'baseApi', { + api = new appsync.GraphqlApi(stack, 'baseApi', { name: 'api', - schemaDefinition: appsync.SchemaDefinition.FILE, - schemaDefinitionFile: path.join(__dirname, 'appsync.test.graphql'), + schema: new appsync.Schema({ + filePath: path.join(__dirname, 'appsync.test.graphql'), + }), }); }); @@ -32,7 +33,7 @@ describe('Rds Data Source configuration', () => { }); cluster = new DatabaseCluster(stack, 'AuroraCluster', { engine: DatabaseClusterEngine.auroraMysql({ version: AuroraMysqlEngineVersion.VER_2_07_1 }), - masterUser: { username: 'clusteradmin' }, + credentials: { username: 'clusteradmin' }, clusterIdentifier: 'db-endpoint-test', instanceProps: { instanceType: InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.SMALL), @@ -162,7 +163,7 @@ describe('adding rds data source from imported api', () => { }); cluster = new DatabaseCluster(stack, 'AuroraCluster', { engine: DatabaseClusterEngine.auroraMysql({ version: AuroraMysqlEngineVersion.VER_2_07_1 }), - masterUser: { username: 'clusteradmin' }, + credentials: { username: 'clusteradmin' }, clusterIdentifier: 'db-endpoint-test', instanceProps: { instanceType: InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.SMALL), @@ -176,7 +177,7 @@ describe('adding rds data source from imported api', () => { test('imported api can add RdsDbDataSource from id', () => { // WHEN - const importedApi = appsync.GraphQLApi.fromGraphqlApiAttributes(stack, 'importedApi', { + const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'importedApi', { graphqlApiId: api.apiId, }); importedApi.addRdsDataSource('ds', cluster, secret); @@ -190,7 +191,7 @@ describe('adding rds data source from imported api', () => { test('imported api can add RdsDataSource from attributes', () => { // WHEN - const importedApi = appsync.GraphQLApi.fromGraphqlApiAttributes(stack, 'importedApi', { + const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'importedApi', { graphqlApiId: api.apiId, graphqlApiArn: api.arn, }); From 8b72a69dae82e7f57f3e9836c894290c8b3d7e42 Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Mon, 19 Oct 2020 14:01:19 +1100 Subject: [PATCH 13/15] fixed appsync tests --- packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts index ac2f4c7f93ef0..ba0d80d00037b 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts @@ -144,7 +144,7 @@ describe('Rds Data Source configuration', () => { }; // THEN - expect(when).toThrow('There is already a Construct with name \'ds\' in GraphQLApi [baseApi]'); + expect(when).toThrow('There is already a Construct with name \'ds\' in GraphqlApi [baseApi]'); }); }); From 8663f6785280b806dadc7f7e652c3ccb1e0e46cd Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Tue, 20 Oct 2020 09:19:50 +1100 Subject: [PATCH 14/15] Apply suggestions from code review Co-authored-by: Bryan Pan --- packages/@aws-cdk/aws-appsync/README.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 381ae9dd3f95e..d2b180549b3ee 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -147,7 +147,6 @@ rdsDS.createResolver({ }); ``` -## Imports #### HTTP Endpoints GraphQL schema file `schema.graphql`: @@ -711,20 +710,6 @@ You can create Object Types in three ways: ``` > This method allows for reusability and modularity, ideal for reducing code duplication. -3. Object Types can be created ***internally*** within the GraphQL API. - ```ts - const api = new appsync.GraphQLApi(stack, 'Api', { - name: 'demo', - schemaDefinition: appsync.SchemaDefinition.CODE, - }); - api.addType('Demo', { - defintion: { - id: appsync.GraphqlType.string({ isRequired: true }), - version: appsync.GraphqlType.string({ isRequired: true }), - }, - }); - ``` - > This method provides easy use and is ideal for smaller projects. To learn more about **Object Types**, read the docs [here](https://graphql.org/learn/schema/#object-types-and-fields). ### Enum Types From c8736df7b8f56c3eea4c80752b6fcad88279dbfa Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Tue, 20 Oct 2020 09:22:54 +1100 Subject: [PATCH 15/15] Update README.md --- packages/@aws-cdk/aws-appsync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index d2b180549b3ee..56c4cf6e0504a 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -99,7 +99,7 @@ const secret = new rds.DatabaseSecret(stack, 'AuroraSecret', { // Create the DB cluster, provide all values needed to customise the database. const cluster = new rds.DatabaseCluster(stack, 'AuroraCluster', { engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_2_07_1 }), - masterUser: { username: 'clusteradmin' }, + credentials: { username: 'clusteradmin' }, clusterIdentifier: 'db-endpoint-test', defaultDatabaseName: 'demos', });