From 68a4e982ba6f82c21b485270ad62b0c60593f4b2 Mon Sep 17 00:00:00 2001 From: Bryan Pan Date: Mon, 7 Dec 2020 12:13:31 -0800 Subject: [PATCH] feat(appsync): support appsync functions for pipelineConfig (#10111) Support AppSync Function by exposing Function Configuration. **BREAKING CHANGES**: `Resolver.pipelineConfig` no longer supports `string[]` - **AppSync**: `Resolver.pipelineConfig` no longer supports `string[]`, instead use `AppsyncFunction` - **AppSync**: Resolvers are scoped from `GraphqlApi` instead of its `Data Source`, this means that the **logical id** of resolvers will change! Fixes #9092 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-appsync/README.md | 38 +- .../aws-appsync/lib/appsync-function.ts | 148 ++++++ .../@aws-cdk/aws-appsync/lib/data-source.ts | 11 +- .../aws-appsync/lib/graphqlapi-base.ts | 16 + packages/@aws-cdk/aws-appsync/lib/index.ts | 1 + packages/@aws-cdk/aws-appsync/lib/resolver.ts | 29 +- .../@aws-cdk/aws-appsync/lib/schema-base.ts | 2 +- .../@aws-cdk/aws-appsync/lib/schema-field.ts | 3 +- .../aws-appsync/lib/schema-intermediate.ts | 37 +- packages/@aws-cdk/aws-appsync/lib/schema.ts | 2 +- packages/@aws-cdk/aws-appsync/package.json | 2 +- .../aws-appsync/test/appsync-lambda.test.ts | 16 + .../test/appsync-object-type.test.ts | 27 +- .../@aws-cdk/aws-appsync/test/appsync.test.ts | 45 +- .../test/integ.api-import.expected.json | 43 +- .../aws-appsync/test/integ.api-import.ts | 25 +- .../test/integ.auth-apikey.expected.json | 4 +- .../test/integ.graphql-iam.expected.json | 6 +- .../test/integ.graphql-schema.expected.json | 4 +- .../aws-appsync/test/integ.graphql-schema.ts | 2 +- .../test/integ.graphql.expected.json | 438 +++++++++--------- 21 files changed, 632 insertions(+), 267 deletions(-) create mode 100644 packages/@aws-cdk/aws-appsync/lib/appsync-function.ts diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index ddd947cf0ab83..794cff6db3cda 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -379,6 +379,38 @@ api.grantMutation(role, 'updateExample'); api.grant(role, appsync.IamResource.ofType('Mutation', 'updateExample'), 'appsync:GraphQL'); ``` +### Pipeline Resolvers and AppSync Functions + +AppSync Functions are local functions that perform certain operations onto a +backend data source. Developers can compose operations (Functions) and execute +them in sequence with Pipeline Resolvers. + +```ts +const appsyncFunction = new appsync.AppsyncFunction(stack, 'function', { + name: 'appsync_function', + api: api, + dataSource: apiDataSource, + requestMappingTemplate: appsync.MappingTemplate.fromFile('request.vtl'), + responseMappingTemplate: appsync.MappingTemplate.fromFile('response.vtl'), +}); +``` + +AppSync Functions are used in tandem with pipeline resolvers to compose multiple +operations. + +```ts +const pipelineResolver = new appsync.Resolver(stack, 'pipeline', { + name: 'pipeline_resolver', + api: api, + dataSource: apiDataSource, + requestMappingTemplate: appsync.MappingTemplate.fromFile('beforeRequest.vtl'), + pipelineConfig: [appsyncFunction], + responseMappingTemplate: appsync.MappingTemplate.fromFile('afterResponse.vtl'), +}); +``` + +Learn more about Pipeline Resolvers and AppSync Functions [here](https://docs.aws.amazon.com/appsync/latest/devguide/pipeline-resolvers.html). + ### Code-First Schema CDK offers the ability to generate your schema in a code-first approach. @@ -543,7 +575,7 @@ To learn more about authorization and directives, read these docs [here](https:/ While `GraphqlType` is a base implementation for GraphQL fields, we have abstractions on top of `GraphqlType` that provide finer grain support. -##### Field +#### Field `Field` extends `GraphqlType` and will allow you to define arguments. [**Interface Types**](#Interface-Types) are not resolvable and this class will allow you to define arguments, but not its resolvers. @@ -570,7 +602,7 @@ const type = new appsync.InterfaceType('Node', { }); ``` -##### Resolvable Fields +#### Resolvable Fields `ResolvableField` extends `Field` and will allow you to define arguments and its resolvers. [**Object Types**](#Object-Types) can have fields that resolve and perform operations on @@ -646,7 +678,7 @@ Intermediate Types include: - [**Input Types**](#Input-Types) - [**Union Types**](#Union-Types) -##### Interface Types +#### Interface Types **Interface Types** are abstract types that define the implementation of other intermediate types. They are useful for eliminating duplication and can be used diff --git a/packages/@aws-cdk/aws-appsync/lib/appsync-function.ts b/packages/@aws-cdk/aws-appsync/lib/appsync-function.ts new file mode 100644 index 0000000000000..7423317d0fa3e --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/lib/appsync-function.ts @@ -0,0 +1,148 @@ +import { Resource, IResource, Lazy, Fn } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnFunctionConfiguration } from './appsync.generated'; +import { BaseDataSource } from './data-source'; +import { IGraphqlApi } from './graphqlapi-base'; +import { MappingTemplate } from './mapping-template'; + +/** + * the base properties for AppSync Functions + */ +export interface BaseAppsyncFunctionProps { + /** + * the name of the AppSync Function + */ + readonly name: string; + /** + * the description for this AppSync Function + * + * @default - no description + */ + readonly description?: string; + /** + * the request mapping template for the AppSync Function + * + * @default - no request mapping template + */ + readonly requestMappingTemplate?: MappingTemplate; + /** + * the response mapping template for the AppSync Function + * + * @default - no response mapping template + */ + readonly responseMappingTemplate?: MappingTemplate; +} + +/** + * the CDK properties for AppSync Functions + */ +export interface AppsyncFunctionProps extends BaseAppsyncFunctionProps { + /** + * the GraphQL Api linked to this AppSync Function + */ + readonly api: IGraphqlApi; + /** + * the data source linked to this AppSync Function + */ + readonly dataSource: BaseDataSource; +} + +/** + * The attributes for imported AppSync Functions + */ +export interface AppsyncFunctionAttributes { + /** + * the ARN of the AppSync function + */ + readonly functionArn: string; +} + +/** + * Interface for AppSync Functions + */ +export interface IAppsyncFunction extends IResource { + /** + * the name of this AppSync Function + * + * @attribute + */ + readonly functionId: string; + /** + * the ARN of the AppSync function + * + * @attribute + */ + readonly functionArn: string; +} + +/** + * AppSync Functions are local functions that perform certain operations + * onto a backend data source. Developers can compose operations (Functions) + * and execute them in sequence with Pipeline Resolvers. + * + * @resource AWS::AppSync::FunctionConfiguration + */ +export class AppsyncFunction extends Resource implements IAppsyncFunction { + /** + * Import Appsync Function from arn + */ + public static fromAppsyncFunctionAttributes(scope: Construct, id: string, attrs: AppsyncFunctionAttributes): IAppsyncFunction { + class Import extends Resource { + public readonly functionId = Lazy.stringValue({ + produce: () => Fn.select(3, Fn.split('/', attrs.functionArn)), + }); + public readonly functionArn = attrs.functionArn; + constructor (s: Construct, i: string) { + super(s, i); + } + } + return new Import(scope, id); + } + + /** + * the name of this AppSync Function + * + * @attribute Name + */ + public readonly functionName: string; + /** + * the ARN of the AppSync function + * + * @attribute + */ + public readonly functionArn: string; + /** + * the ID of the AppSync function + * + * @attribute + */ + public readonly functionId: string; + /** + * the data source of this AppSync Function + * + * @attribute DataSourceName + */ + public readonly dataSource: BaseDataSource; + + private readonly function: CfnFunctionConfiguration; + + public constructor(scope: Construct, id: string, props: AppsyncFunctionProps) { + super(scope, id); + this.function = new CfnFunctionConfiguration(this, 'Resource', { + name: props.name, + description: props.description, + apiId: props.api.apiId, + dataSourceName: props.dataSource.name, + functionVersion: '2018-05-29', + requestMappingTemplate: props.requestMappingTemplate?.renderTemplate(), + responseMappingTemplate: props.responseMappingTemplate?.renderTemplate(), + }); + this.functionName = this.function.attrName; + this.functionArn = this.function.attrFunctionArn; + this.functionId = this.function.attrFunctionId; + this.dataSource = props.dataSource; + + this.function.addDependsOn(this.dataSource.ds); + props.api.addSchemaDependency(this.function); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/lib/data-source.ts b/packages/@aws-cdk/aws-appsync/lib/data-source.ts index cb6c42de28f14..96c578390dfbc 100644 --- a/packages/@aws-cdk/aws-appsync/lib/data-source.ts +++ b/packages/@aws-cdk/aws-appsync/lib/data-source.ts @@ -5,6 +5,7 @@ import { IDatabaseCluster } from '@aws-cdk/aws-rds'; import { ISecret } from '@aws-cdk/aws-secretsmanager'; import { IResolvable, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; +import { BaseAppsyncFunctionProps, AppsyncFunction } from './appsync-function'; import { CfnDataSource } from './appsync.generated'; import { IGraphqlApi } from './graphqlapi-base'; import { BaseResolverProps, Resolver } from './resolver'; @@ -125,7 +126,14 @@ export abstract class BaseDataSource extends CoreConstruct { * creates a new resolver for this datasource and API using the given properties */ public createResolver(props: BaseResolverProps): Resolver { - return new Resolver(this, `${props.typeName}${props.fieldName}Resolver`, { + return this.api.createResolver({ dataSource: this, ...props }); + } + + /** + * creates a new appsync function for this datasource and API using the given properties + */ + public createFunction(props: BaseAppsyncFunctionProps): AppsyncFunction { + return new AppsyncFunction(this, `${props.name}Function`, { api: this.api, dataSource: this, ...props, @@ -172,7 +180,6 @@ export class NoneDataSource extends BaseDataSource { export interface DynamoDbDataSourceProps extends BackedDataSourceProps { /** * The DynamoDB table backing this data source - * [disable-awslint:ref-via-interface] */ readonly table: ITable; /** diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts index 288384c6454a5..3b037101e97ee 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts @@ -4,6 +4,7 @@ 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, RdsDataSource, AwsIamConfig } from './data-source'; +import { Resolver, ExtendedResolverProps } from './resolver'; /** * Optional configuration for data sources @@ -107,6 +108,11 @@ export interface IGraphqlApi extends IResource { options?: DataSourceOptions ): RdsDataSource; + /** + * creates a new resolver for this datasource and API using the given properties + */ + createResolver(props: ExtendedResolverProps): Resolver; + /** * Add schema dependency if not imported * @@ -217,6 +223,16 @@ export abstract class GraphqlApiBase extends Resource implements IGraphqlApi { }); } + /** + * creates a new resolver for this datasource and API using the given properties + */ + public createResolver(props: ExtendedResolverProps): Resolver { + return new Resolver(this, `${props.typeName}${props.fieldName}Resolver`, { + api: this, + ...props, + }); + } + /** * Add schema dependency if not imported * diff --git a/packages/@aws-cdk/aws-appsync/lib/index.ts b/packages/@aws-cdk/aws-appsync/lib/index.ts index c4e05e9a5300e..5b0f12cf75844 100644 --- a/packages/@aws-cdk/aws-appsync/lib/index.ts +++ b/packages/@aws-cdk/aws-appsync/lib/index.ts @@ -1,4 +1,5 @@ // AWS::AppSync CloudFormation Resources: +export * from './appsync-function'; export * from './appsync.generated'; export * from './key'; export * from './data-source'; diff --git a/packages/@aws-cdk/aws-appsync/lib/resolver.ts b/packages/@aws-cdk/aws-appsync/lib/resolver.ts index 91abfea5f3cb4..eb65a59f494fd 100644 --- a/packages/@aws-cdk/aws-appsync/lib/resolver.ts +++ b/packages/@aws-cdk/aws-appsync/lib/resolver.ts @@ -1,4 +1,5 @@ import { Construct } from 'constructs'; +import { IAppsyncFunction } from './appsync-function'; import { CfnResolver } from './appsync.generated'; import { BaseDataSource } from './data-source'; import { IGraphqlApi } from './graphqlapi-base'; @@ -26,7 +27,7 @@ export interface BaseResolverProps { * @default - no pipeline resolver configuration * An empty array | undefined sets resolver to be of kind, unit */ - readonly pipelineConfig?: string[]; + readonly pipelineConfig?: IAppsyncFunction[]; /** * The request mapping template for this resolver * @@ -42,13 +43,9 @@ export interface BaseResolverProps { } /** - * Additional properties for an AppSync resolver like GraphQL API reference and datasource + * Additional property for an AppSync resolver for data source reference */ -export interface ResolverProps extends BaseResolverProps { - /** - * The API this resolver is attached to - */ - readonly api: IGraphqlApi; +export interface ExtendedResolverProps extends BaseResolverProps { /** * The data source this resolver is using * @@ -57,6 +54,16 @@ export interface ResolverProps extends BaseResolverProps { readonly dataSource?: BaseDataSource; } +/** + * Additional property for an AppSync resolver for GraphQL API reference + */ +export interface ResolverProps extends ExtendedResolverProps { + /** + * The API this resolver is attached to + */ + readonly api: IGraphqlApi; +} + /** * An AppSync resolver */ @@ -71,7 +78,13 @@ export class Resolver extends CoreConstruct { constructor(scope: Construct, id: string, props: ResolverProps) { super(scope, id); - const pipelineConfig = props.pipelineConfig && props.pipelineConfig.length ? { functions: props.pipelineConfig } : undefined; + const pipelineConfig = props.pipelineConfig && props.pipelineConfig.length ? + { functions: props.pipelineConfig.map((func) => func.functionId) } + : undefined; + + if (pipelineConfig && props.dataSource) { + throw new Error(`Pipeline Resolver cannot have data source. Received: ${props.dataSource.name}`); + } this.resolver = new CfnResolver(this, 'Resource', { apiId: props.api.apiId, diff --git a/packages/@aws-cdk/aws-appsync/lib/schema-base.ts b/packages/@aws-cdk/aws-appsync/lib/schema-base.ts index a639c747ca208..b824d2b29c4e6 100644 --- a/packages/@aws-cdk/aws-appsync/lib/schema-base.ts +++ b/packages/@aws-cdk/aws-appsync/lib/schema-base.ts @@ -142,7 +142,7 @@ export interface IIntermediateType { /** * Method called when the stringifying Intermediate Types for schema generation * - * @param api The binding GraphQL Api [disable-awslint:ref-via-interface] + * @param api The binding GraphQL Api * * @internal */ diff --git a/packages/@aws-cdk/aws-appsync/lib/schema-field.ts b/packages/@aws-cdk/aws-appsync/lib/schema-field.ts index ed7c8790818db..b276644873a7d 100644 --- a/packages/@aws-cdk/aws-appsync/lib/schema-field.ts +++ b/packages/@aws-cdk/aws-appsync/lib/schema-field.ts @@ -1,3 +1,4 @@ +import { IAppsyncFunction } from './appsync-function'; import { BaseDataSource } from './data-source'; import { AuthorizationType } from './graphqlapi'; import { MappingTemplate } from './mapping-template'; @@ -423,7 +424,7 @@ export interface ResolvableFieldOptions extends FieldOptions { * @default - no pipeline resolver configuration * An empty array or undefined prop will set resolver to be of type unit */ - readonly pipelineConfig?: string[]; + readonly pipelineConfig?: IAppsyncFunction[]; /** * The request mapping template for this resolver * diff --git a/packages/@aws-cdk/aws-appsync/lib/schema-intermediate.ts b/packages/@aws-cdk/aws-appsync/lib/schema-intermediate.ts index cc778aee51a50..5d87ba2e7f130 100644 --- a/packages/@aws-cdk/aws-appsync/lib/schema-intermediate.ts +++ b/packages/@aws-cdk/aws-appsync/lib/schema-intermediate.ts @@ -1,8 +1,9 @@ import { AuthorizationType, GraphqlApi } from './graphqlapi'; +import { IGraphqlApi } from './graphqlapi-base'; import { shapeAddition } from './private'; import { Resolver } from './resolver'; import { Directive, IField, IIntermediateType, AddFieldOptions } from './schema-base'; -import { BaseTypeOptions, GraphqlType, ResolvableFieldOptions } from './schema-field'; +import { BaseTypeOptions, GraphqlType, ResolvableFieldOptions, ResolvableField } from './schema-field'; /** * Properties for configuring an Intermediate Type @@ -159,11 +160,27 @@ export class ObjectType extends InterfaceType implements IIntermediateType { super(name, options); this.interfaceTypes = props.interfaceTypes; this.resolvers = []; + } + /** + * Method called when the stringifying Intermediate Types for schema generation + * + * @internal + */ + public _bindToGraphqlApi(api: GraphqlApi): IIntermediateType { + this.modes = api.modes; + // If the resolvers have been generated, skip the bind + if (this.resolvers && this.resolvers.length > 0) { + return this; + } Object.keys(this.definition).forEach((fieldName) => { const field = this.definition[fieldName]; - this.generateResolver(fieldName, field.fieldOptions); + if (field instanceof ResolvableField) { + if (!this.resolvers) this.resolvers = []; + this.resolvers.push(this.generateResolver(api, fieldName, field.fieldOptions)); + } }); + return this; } /** @@ -177,7 +194,6 @@ export class ObjectType extends InterfaceType implements IIntermediateType { if (!options.fieldName || !options.field) { throw new Error('Object Types must have both fieldName and field options.'); } - this.generateResolver(options.fieldName, options.field.fieldOptions); this.definition[options.fieldName] = options.field; } @@ -201,16 +217,15 @@ export class ObjectType extends InterfaceType implements IIntermediateType { /** * Generate the resolvers linked to this Object Type */ - protected generateResolver(fieldName: string, options?: ResolvableFieldOptions): void { - if (!options?.dataSource) return; - if (!this.resolvers) { this.resolvers = []; } - this.resolvers.push(options.dataSource.createResolver({ + protected generateResolver(api: IGraphqlApi, fieldName: string, options?: ResolvableFieldOptions): Resolver { + return api.createResolver({ typeName: this.name, fieldName: fieldName, - pipelineConfig: options.pipelineConfig, - requestMappingTemplate: options.requestMappingTemplate, - responseMappingTemplate: options.responseMappingTemplate, - })); + dataSource: options?.dataSource, + pipelineConfig: options?.pipelineConfig, + requestMappingTemplate: options?.requestMappingTemplate, + responseMappingTemplate: options?.responseMappingTemplate, + }); } } diff --git a/packages/@aws-cdk/aws-appsync/lib/schema.ts b/packages/@aws-cdk/aws-appsync/lib/schema.ts index 3ab8a800d16fb..1ae6964bda346 100644 --- a/packages/@aws-cdk/aws-appsync/lib/schema.ts +++ b/packages/@aws-cdk/aws-appsync/lib/schema.ts @@ -81,7 +81,7 @@ export class Schema { apiId: api.apiId, definition: this.mode === SchemaMode.CODE ? Lazy.string({ - produce: () => this.types.reduce((acc, type) => { return `${acc}${type._bindToGraphqlApi(api).toString()}\n`; }, + produce: () => this.types.reduce((acc, type) => `${acc}${type._bindToGraphqlApi(api).toString()}\n`, `${this.declareSchema()}${this.definition}`), }) : this.definition, diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index 906bf0f2ef751..3d55d00b96177 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -115,7 +115,7 @@ "no-unused-type:@aws-cdk/aws-appsync.UserPoolConfig", "no-unused-type:@aws-cdk/aws-appsync.UserPoolDefaultAction", "props-physical-name:@aws-cdk/aws-appsync.GraphqlApiProps", - "from-method:@aws-cdk/aws-appsync.GraphqlApi" + "props-physical-name:@aws-cdk/aws-appsync.AppsyncFunctionProps" ] }, "stability": "experimental", diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-lambda.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-lambda.test.ts index 0cc8396382017..11190591974a8 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-lambda.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-lambda.test.ts @@ -72,6 +72,22 @@ describe('Lambda Data Source configuration', () => { api.addLambdaDataSource('ds', func); }).toThrow("There is already a Construct with name 'ds' in GraphqlApi [baseApi]"); }); + + test('lambda data sources dont require mapping templates', () => { + // WHEN + const ds = api.addLambdaDataSource('ds', func, { + name: 'custom', + description: 'custom description', + }); + + ds.createResolver({ + typeName: 'test', + fieldName: 'field', + }); + + // THEN + expect(stack).toHaveResource('AWS::AppSync::Resolver'); + }); }); describe('adding lambda data source from imported api', () => { diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-object-type.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-object-type.test.ts index 0a6c45d24fd85..ef05e857c8eb6 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-object-type.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-object-type.test.ts @@ -100,6 +100,7 @@ describe('testing Object Type properties', () => { args: { arg: t.int, }, + }); const test = new appsync.ObjectType('Test', { definition: { @@ -124,6 +125,7 @@ describe('testing Object Type properties', () => { args: { arg: t.int, }, + }); const test = new appsync.ObjectType('Test', { definition: { @@ -142,15 +144,27 @@ describe('testing Object Type properties', () => { test('Object Type can implement Resolvable Field for pipelineResolvers', () => { // WHEN + const ds = api.addNoneDataSource('none'); + const test1 = ds.createFunction({ + name: 'test1', + }); + const test2 = ds.createFunction({ + name: 'test2', + }); const test = new appsync.ObjectType('Test', { definition: { resolve: new appsync.ResolvableField({ returnType: t.string, - dataSource: api.addNoneDataSource('none'), args: { arg: t.int, }, - pipelineConfig: ['test', 'test'], + pipelineConfig: [test1, test2], + requestMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: '2017-02-28', + })), + responseMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: 'v1', + })), }), }, }); @@ -159,7 +173,12 @@ describe('testing Object Type properties', () => { // THEN expect(stack).toHaveResourceLike('AWS::AppSync::Resolver', { Kind: 'PIPELINE', - PipelineConfig: { Functions: ['test', 'test'] }, + PipelineConfig: { + Functions: [ + { 'Fn::GetAtt': ['apinonetest1FunctionEF63046F', 'FunctionId'] }, + { 'Fn::GetAtt': ['apinonetest2Function615111D0', 'FunctionId'] }, + ], + }, }); }); @@ -169,6 +188,7 @@ describe('testing Object Type properties', () => { returnType: t.string, dataSource: api.addNoneDataSource('none'), args: { arg: t.int }, + }); const test = new appsync.ObjectType('Test', { definition: { @@ -227,6 +247,7 @@ describe('testing Object Type properties', () => { args: { arg: t.string, }, + }); test.addField({ fieldName: 'resolve', field }); diff --git a/packages/@aws-cdk/aws-appsync/test/appsync.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync.test.ts index 252051de6f0c6..ca496e99afbff 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync.test.ts @@ -18,24 +18,58 @@ beforeEach(() => { test('appsync should configure pipeline when pipelineConfig has contents', () => { // WHEN - new appsync.Resolver(stack, 'resolver', { - api: api, + const ds = api.addNoneDataSource('none'); + const test1 = ds.createFunction({ + name: 'test1', + }); + const test2 = ds.createFunction({ + name: 'test2', + }); + api.createResolver({ typeName: 'test', fieldName: 'test2', - pipelineConfig: ['test', 'test'], + pipelineConfig: [test1, test2], }); // THEN expect(stack).toHaveResourceLike('AWS::AppSync::Resolver', { Kind: 'PIPELINE', - PipelineConfig: { Functions: ['test', 'test'] }, + PipelineConfig: { + Functions: [ + { 'Fn::GetAtt': ['apinonetest1FunctionEF63046F', 'FunctionId'] }, + { 'Fn::GetAtt': ['apinonetest2Function615111D0', 'FunctionId'] }, + ], + }, + }); +}); + +test('appsync should error when creating pipeline resolver with data source', () => { + // WHEN + const ds = api.addNoneDataSource('none'); + const test1 = ds.createFunction({ + name: 'test1', }); + const test2 = ds.createFunction({ + name: 'test2', + }); + + // THEN + expect(() => { + api.createResolver({ + dataSource: ds, + typeName: 'test', + fieldName: 'test2', + pipelineConfig: [test1, test2], + }); + }).toThrowError('Pipeline Resolver cannot have data source. Received: none'); }); test('appsync should configure resolver as unit when pipelineConfig is empty', () => { // WHEN + const ds = api.addNoneDataSource('none'); new appsync.Resolver(stack, 'resolver', { api: api, + dataSource: ds, typeName: 'test', fieldName: 'test2', }); @@ -48,8 +82,7 @@ test('appsync should configure resolver as unit when pipelineConfig is empty', ( test('appsync should configure resolver as unit when pipelineConfig is empty array', () => { // WHEN - new appsync.Resolver(stack, 'resolver', { - api: api, + api.createResolver({ typeName: 'test', fieldName: 'test2', pipelineConfig: [], diff --git a/packages/@aws-cdk/aws-appsync/test/integ.api-import.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.api-import.expected.json index c8692067ef47a..1a760c0d715a1 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.api-import.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.api-import.expected.json @@ -134,7 +134,7 @@ } } }, - "ApidsQuerygetTestsResolver952F49EE": { + "ApiQuerygetTestsResolver025B8E0A": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -151,7 +151,7 @@ "Apids0DB53FEA" ] }, - "ApidsMutationaddTestResolverBCF0400B": { + "ApiMutationaddTestResolver7A08AE91": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -197,6 +197,45 @@ "Name": "none", "Type": "NONE" } + }, + "api2nonepipelinefunctionFunction2426F465": { + "Type": "AWS::AppSync::FunctionConfiguration", + "Properties": { + "ApiId": { + "Fn::ImportValue": "baseStack:ExportsOutputFnGetAttbaseApiCDA4D43AApiId50287E68" + }, + "DataSourceName": "none", + "FunctionVersion": "2018-05-29", + "Name": "pipeline_function", + "RequestMappingTemplate": "{\"version\":\"2017-02-28\"}", + "ResponseMappingTemplate": "{\"version\":\"v1\"}" + }, + "DependsOn": [ + "api2noneC88DB89F" + ] + }, + "pipelineresolver843133EA": { + "Type": "AWS::AppSync::Resolver", + "Properties": { + "ApiId": { + "Fn::ImportValue": "baseStack:ExportsOutputFnGetAttbaseApiCDA4D43AApiId50287E68" + }, + "FieldName": "version", + "TypeName": "test", + "Kind": "PIPELINE", + "PipelineConfig": { + "Functions": [ + { + "Fn::GetAtt": [ + "api2nonepipelinefunctionFunction2426F465", + "FunctionId" + ] + } + ] + }, + "RequestMappingTemplate": "{\"version\":\"2017-02-28\"}", + "ResponseMappingTemplate": "{\"version\":\"v1\"}" + } } } } diff --git a/packages/@aws-cdk/aws-appsync/test/integ.api-import.ts b/packages/@aws-cdk/aws-appsync/test/integ.api-import.ts index 8781f83fe117e..a4ebc3105209e 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.api-import.ts +++ b/packages/@aws-cdk/aws-appsync/test/integ.api-import.ts @@ -62,6 +62,29 @@ const api2 = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'api2', { graphqlApiArn: baseApi.arn, }); -api2.addNoneDataSource('none'); +const none = api2.addNoneDataSource('none'); + +const func = none.createFunction({ + name: 'pipeline_function', + requestMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: '2017-02-28', + })), + responseMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: 'v1', + })), +}); + +new appsync.Resolver(stack, 'pipeline_resolver', { + api: api2, + typeName: 'test', + fieldName: 'version', + pipelineConfig: [func], + requestMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: '2017-02-28', + })), + responseMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: 'v1', + })), +}); app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.auth-apikey.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.auth-apikey.expected.json index 17fec6fbed787..62c6b75e725d4 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.auth-apikey.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.auth-apikey.expected.json @@ -120,7 +120,7 @@ } } }, - "ApitestDataSourceQuerygetTestsResolverA3BBB672": { + "ApiQuerygetTestsResolver025B8E0A": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -141,7 +141,7 @@ "ApitestDataSource96AE54D5" ] }, - "ApitestDataSourceMutationaddTestResolver36203D6B": { + "ApiMutationaddTestResolver7A08AE91": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json index eda4a662ffc58..1788c675b2c67 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json @@ -149,7 +149,7 @@ } } }, - "ApidsQuerygetTestResolverCCED7EC2": { + "ApiQuerygetTestResolver4C1F8B0C": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -170,7 +170,7 @@ "ApiSchema510EECD7" ] }, - "ApidsQuerygetTestsResolver952F49EE": { + "ApiQuerygetTestsResolver025B8E0A": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -191,7 +191,7 @@ "ApiSchema510EECD7" ] }, - "ApidsMutationaddTestResolverBCF0400B": { + "ApiMutationaddTestResolver7A08AE91": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.expected.json index 1e49940a8f92a..60e71fe2c0293 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.expected.json @@ -119,7 +119,7 @@ } } }, - "codefirstapiplanetsQuerygetPlanetsResolver633EA597": { + "codefirstapiQuerygetPlanetsResolver54E13D9A": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -140,7 +140,7 @@ "codefirstapiSchema148B6CDE" ] }, - "codefirstapiplanetsMutationaddPlanetResolver155AF87E": { + "codefirstapiMutationaddPlanetResolver49A375A3": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.ts b/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.ts index 4c02dd07f4a5a..6475b0bfae971 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.ts +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.ts @@ -103,7 +103,7 @@ api.addMutation('addPlanet', new appsync.ResolvableField({ responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultItem(), })); -api.addSubscription('addedPlanets', new appsync.ResolvableField({ +api.addSubscription('addedPlanets', new appsync.Field({ returnType: planet.attribute(), args: { id: ScalarType.required_id }, directives: [appsync.Directive.subscribe('addPlanet')], 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 503471f25c9a6..be40ad91870b7 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json @@ -90,7 +90,7 @@ "Type": "NONE" } }, - "ApinoneQuerygetServiceVersionResolver336A3C2C": { + "ApiQuerygetServiceVersionResolver269A74C1": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -197,7 +197,202 @@ } } }, - "ApicustomerDsQuerygetCustomersResolverA74C8A2E": { + "ApiorderDsServiceRoleCC2040C0": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appsync.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ApiorderDsServiceRoleDefaultPolicy3315FCF4": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "OrderTable416EB896", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "OrderTable416EB896", + "Arn" + ] + }, + "/index/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ApiorderDsServiceRoleDefaultPolicy3315FCF4", + "Roles": [ + { + "Ref": "ApiorderDsServiceRoleCC2040C0" + } + ] + } + }, + "ApiorderDsB50C8AAD": { + "Type": "AWS::AppSync::DataSource", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "ApiF70053CD", + "ApiId" + ] + }, + "Name": "Order", + "Type": "AMAZON_DYNAMODB", + "DynamoDBConfig": { + "AwsRegion": { + "Ref": "AWS::Region" + }, + "TableName": { + "Ref": "OrderTable416EB896" + } + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "ApiorderDsServiceRoleCC2040C0", + "Arn" + ] + } + } + }, + "ApipaymentDsServiceRole0DAC58D6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appsync.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ApipaymentDsServiceRoleDefaultPolicy528E42B0": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":dynamodb:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":table/PaymentTable" + ] + ] + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ApipaymentDsServiceRoleDefaultPolicy528E42B0", + "Roles": [ + { + "Ref": "ApipaymentDsServiceRole0DAC58D6" + } + ] + } + }, + "ApipaymentDs95C7AC36": { + "Type": "AWS::AppSync::DataSource", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "ApiF70053CD", + "ApiId" + ] + }, + "Name": "Payment", + "Type": "AMAZON_DYNAMODB", + "DynamoDBConfig": { + "AwsRegion": { + "Ref": "AWS::Region" + }, + "TableName": "PaymentTable" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "ApipaymentDsServiceRole0DAC58D6", + "Arn" + ] + } + } + }, + "ApiQuerygetCustomersResolver522EE433": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -218,7 +413,7 @@ "ApiSchema510EECD7" ] }, - "ApicustomerDsQuerygetCustomerResolver3649A130": { + "ApiQuerygetCustomerResolver007520DC": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -239,7 +434,7 @@ "ApiSchema510EECD7" ] }, - "ApicustomerDsMutationaddCustomerResolver4DE5B517": { + "ApiMutationaddCustomerResolver53321A05": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -260,7 +455,7 @@ "ApiSchema510EECD7" ] }, - "ApicustomerDsMutationsaveCustomerResolver241DD231": { + "ApiMutationsaveCustomerResolver85516C23": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -281,7 +476,7 @@ "ApiSchema510EECD7" ] }, - "ApicustomerDsMutationsaveCustomerWithFirstOrderResolver7DE2CBC8": { + "ApiMutationsaveCustomerWithFirstOrderResolver66DBDFD0": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -302,7 +497,7 @@ "ApiSchema510EECD7" ] }, - "ApicustomerDsMutationremoveCustomerResolverAD3AE7F5": { + "ApiMutationremoveCustomerResolver8435F803": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -323,104 +518,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsServiceRoleCC2040C0": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "appsync.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } - } - }, - "ApiorderDsServiceRoleDefaultPolicy3315FCF4": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "dynamodb:BatchGetItem", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - "dynamodb:Query", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:UpdateItem", - "dynamodb:DeleteItem" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "OrderTable416EB896", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "OrderTable416EB896", - "Arn" - ] - }, - "/index/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "ApiorderDsServiceRoleDefaultPolicy3315FCF4", - "Roles": [ - { - "Ref": "ApiorderDsServiceRoleCC2040C0" - } - ] - } - }, - "ApiorderDsB50C8AAD": { - "Type": "AWS::AppSync::DataSource", - "Properties": { - "ApiId": { - "Fn::GetAtt": [ - "ApiF70053CD", - "ApiId" - ] - }, - "Name": "Order", - "Type": "AMAZON_DYNAMODB", - "DynamoDBConfig": { - "AwsRegion": { - "Ref": "AWS::Region" - }, - "TableName": { - "Ref": "OrderTable416EB896" - } - }, - "ServiceRoleArn": { - "Fn::GetAtt": [ - "ApiorderDsServiceRoleCC2040C0", - "Arn" - ] - } - } - }, - "ApiorderDsQuerygetCustomerOrdersEqResolverEF9D5350": { + "ApiQuerygetCustomerOrdersEqResolver6DA88A11": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -441,7 +539,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetOrderCustomersEqResolverE58570FF": { + "ApiQuerygetOrderCustomersEqResolverA8612570": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -462,7 +560,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetCustomerOrdersLtResolver909F3D8F": { + "ApiQuerygetCustomerOrdersLtResolver284B37E8": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -483,7 +581,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetOrderCustomersLtResolver77468800": { + "ApiQuerygetOrderCustomersLtResolver618A88A9": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -504,7 +602,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetCustomerOrdersLeResolverF230A8BE": { + "ApiQuerygetCustomerOrdersLeResolverFB2A506A": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -525,7 +623,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetOrderCustomersLeResolver836A0389": { + "ApiQuerygetOrderCustomersLeResolverFAA205B9": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -546,7 +644,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetCustomerOrdersGtResolverF01F806B": { + "ApiQuerygetCustomerOrdersGtResolver13F90EA5": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -567,7 +665,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetOrderCustomersGtResolver3197CCFE": { + "ApiQuerygetOrderCustomersGtResolver9487ECD9": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -588,7 +686,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetCustomerOrdersGeResolver63CAD303": { + "ApiQuerygetCustomerOrdersGeResolver2A181899": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -609,7 +707,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetOrderCustomersGeResolver0B78B0B4": { + "ApiQuerygetOrderCustomersGeResolver8441E608": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -630,7 +728,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetCustomerOrdersFilterResolverCD2B8747": { + "ApiQuerygetCustomerOrdersFilterResolver96245084": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -651,7 +749,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetCustomerOrdersBetweenResolver7DEE368E": { + "ApiQuerygetCustomerOrdersBetweenResolver97B6D708": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -672,7 +770,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetOrderCustomersFilterResolver628CC68D": { + "ApiQuerygetOrderCustomersFilterResolverF1015ABD": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -693,7 +791,7 @@ "ApiSchema510EECD7" ] }, - "ApiorderDsQuerygetOrderCustomersBetweenResolver2048F3CB": { + "ApiQuerygetOrderCustomersBetweenResolver92680CEC": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -714,105 +812,7 @@ "ApiSchema510EECD7" ] }, - "ApipaymentDsServiceRole0DAC58D6": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "appsync.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } - } - }, - "ApipaymentDsServiceRoleDefaultPolicy528E42B0": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "dynamodb:BatchGetItem", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - "dynamodb:Query", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:UpdateItem", - "dynamodb:DeleteItem" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":dynamodb:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":table/PaymentTable" - ] - ] - }, - { - "Ref": "AWS::NoValue" - } - ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "ApipaymentDsServiceRoleDefaultPolicy528E42B0", - "Roles": [ - { - "Ref": "ApipaymentDsServiceRole0DAC58D6" - } - ] - } - }, - "ApipaymentDs95C7AC36": { - "Type": "AWS::AppSync::DataSource", - "Properties": { - "ApiId": { - "Fn::GetAtt": [ - "ApiF70053CD", - "ApiId" - ] - }, - "Name": "Payment", - "Type": "AMAZON_DYNAMODB", - "DynamoDBConfig": { - "AwsRegion": { - "Ref": "AWS::Region" - }, - "TableName": "PaymentTable" - }, - "ServiceRoleArn": { - "Fn::GetAtt": [ - "ApipaymentDsServiceRole0DAC58D6", - "Arn" - ] - } - } - }, - "ApipaymentDsQuerygetPaymentResolverD172BFC9": { + "ApiQuerygetPaymentResolver29598AC3": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -833,7 +833,7 @@ "ApiSchema510EECD7" ] }, - "ApipaymentDsMutationsavePaymentResolverE09FE5BB": { + "ApiMutationsavePaymentResolver03088E76": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { @@ -893,7 +893,7 @@ } } }, - "ApidsMutationdoPostOnAwsResolver9583D8A3": { + "ApiMutationdoPostOnAwsResolverEDACEA42": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": {