diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 88f059fc30b95..104be3e026e12 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -225,7 +225,7 @@ api.grantMutation(role, 'updateExample'); api.grant(role, appsync.IamResource.ofType('Mutation', 'updateExample'), 'appsync:GraphQL'); ``` -## Code-First Schema +### Code-First Schema CDK offers the ability to generate your schema in a code-first approach. A code-first approach offers a developer workflow with: @@ -235,7 +235,7 @@ A code-first approach offers a developer workflow with: The code-first approach allows for **dynamic** schema generation. You can generate your schema based on variables and templates to reduce code duplication. -### Code-First Example +#### Code-First Example To showcase the code-first approach. Let's try to model the following schema segment. @@ -331,15 +331,13 @@ this.objectTypes = [ schema.Node, schema.Film ]; const filmConnections = schema.generateEdgeAndConnection(schema.Film); -api.addType('Query', { - definition: { - allFilms: new appsync.ResolvableField(dummyDataSource, { - returnType: filmConnections.connection.attribute(), - args: schema.args, - requestMappingTemplate: dummyRequest, - responseMappingTemplate: dummyResponse, - }), - } +api.addQuery('allFilms', new appsync.ResolvableField({ + returnType: filmConnections.connection.attribute(), + args: schema.args, + dataSource: dummyDataSource, + requestMappingTemplate: dummyRequest, + responseMappingTemplate: dummyResponse, + }), }); this.objectTypes.map((t) => api.addType(t)); @@ -353,7 +351,7 @@ create the base Object Type (i.e. Film) and from there we can generate its respe Check out a more in-depth example [here](https://github.com/BryanPan342/starwars-code-first). -### GraphQL Types +#### GraphQL Types One of the benefits of GraphQL is its strongly typed nature. We define the types within an object, query, mutation, interface, etc. as **GraphQL Types**. @@ -369,12 +367,12 @@ More concretely, GraphQL Types are simply the types appended to variables. Referencing the object type `Demo` in the previous example, the GraphQL Types is `String!` and is applied to both the names `id` and `version`. -### Field and Resolvable Fields +#### Field and Resolvable Fields 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. @@ -401,7 +399,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 @@ -463,7 +461,7 @@ const query = new appsync.ObjectType('Query', { Learn more about fields and resolvers [here](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-overview.html). -### Intermediate Types +#### Intermediate Types Intermediate Types are defined by Graphql Types and Fields. They have a set of defined fields, where each field corresponds to another type in the system. Intermediate @@ -473,7 +471,7 @@ Intermediate Types include: - [**Interface Types**](#Interface-Types) - [**Object Types**](#Object-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 @@ -488,7 +486,7 @@ const node = new appsync.InterfaceType('Node', { }); ``` -### Object Types +##### Object Types **Object Types** are types that you declare. For example, in the [code-first example](#code-first-example) the `demo` variable is an **Object Type**. **Object Types** are defined by @@ -565,3 +563,48 @@ You can create Object Types in three ways: ``` > This method provides easy use and is ideal for smaller projects. +#### Query + +Every schema requires a top level Query type. By default, the schema will look +for the `Object Type` named `Query`. The top level `Query` is the **only** exposed +type that users can access to perform `GET` operations on your Api. + +To add fields for these queries, we can simply run the `addQuery` function to add +to the schema's `Query` type. + +```ts +const string = appsync.GraphqlType.string(); +const int = appsync.GraphqlType.int(); +api.addQuery('allFilms', new appsync.ResolvableField({ + returnType: filmConnection.attribute(), + args: { after: string, first: int, before: string, last: int}, + dataSource: api.addNoneDataSource('none'), + requestMappingTemplate: dummyRequest, + responseMappingTemplate: dummyResponse, +})); +``` + +To learn more about top level operations, check out the docs [here](https://docs.aws.amazon.com/appsync/latest/devguide/graphql-overview.html). + +#### Mutation + +Every schema **can** have a top level Mutation type. By default, the schema will look +for the `Object Type` named `Mutation`. The top level `Mutation` Type is the only exposed +type that users can access to perform `mutable` operations on your Api. + +To add fields for these mutations, we can simply run the `addMutation` function to add +to the schema's `Mutation` type. + +```ts +const string = appsync.GraphqlType.string(); +const int = appsync.GraphqlType.int(); +api.addMutation('addFilm', new appsync.ResolvableField({ + returnType: film.attribute(), + args: { name: string, film_number: int }, + dataSource: api.addNoneDataSource('none'), + requestMappingTemplate: dummyRequest, + responseMappingTemplate: dummyResponse, +})); +``` + +To learn more about top level operations, check out the docs [here](https://docs.aws.amazon.com/appsync/latest/devguide/graphql-overview.html). diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index effa59310b6bc..4251336f017ea 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -5,6 +5,8 @@ import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema } from './appsync.generated' import { IGraphqlApi, GraphqlApiBase } from './graphqlapi-base'; import { Schema } from './schema'; import { IIntermediateType } from './schema-base'; +import { ResolvableField } from './schema-field'; +import { ObjectType } from './schema-intermediate'; /** * enum with all possible values for AppSync authorization type @@ -588,4 +590,34 @@ export class GraphQLApi extends GraphqlApiBase { public addType(type: IIntermediateType): IIntermediateType { return this.schema.addType(type); } + + /** + * Add a query field to the schema's Query. If one isn't set by + * the user, CDK will create an Object Type called 'Query'. For example, + * + * type Query { + * fieldName: Field.returnType + * } + * + * @param fieldName the name of the query + * @param field the resolvable field to for this query + */ + public addQuery(fieldName: string, field: ResolvableField): ObjectType { + return this.schema.addQuery(fieldName, field); + } + + /** + * Add a mutation field to the schema's Mutation. If one isn't set by + * the user, CDK will create an Object Type called 'Mutation'. For example, + * + * type Mutation { + * fieldName: Field.returnType + * } + * + * @param fieldName the name of the Mutation + * @param field the resolvable field to for this Mutation + */ + public addMutation(fieldName: string, field: ResolvableField): ObjectType { + return this.schema.addMutation(fieldName, field); + } } diff --git a/packages/@aws-cdk/aws-appsync/lib/schema.ts b/packages/@aws-cdk/aws-appsync/lib/schema.ts index 7d3df6b569ae1..f7751f38a3182 100644 --- a/packages/@aws-cdk/aws-appsync/lib/schema.ts +++ b/packages/@aws-cdk/aws-appsync/lib/schema.ts @@ -2,8 +2,10 @@ import { readFileSync } from 'fs'; import { Lazy } from '@aws-cdk/core'; import { CfnGraphQLSchema } from './appsync.generated'; import { GraphQLApi } from './graphqlapi'; -import { SchemaMode } from './private'; +import { SchemaMode, shapeAddition } from './private'; import { IIntermediateType } from './schema-base'; +import { ResolvableField } from './schema-field'; +import { ObjectType } from './schema-intermediate'; /** * The options for configuring a schema @@ -44,7 +46,13 @@ export class Schema { */ public definition: string; - protected schema?: CfnGraphQLSchema; + private query?: ObjectType; + + private mutation?: ObjectType; + + private subscription?: ObjectType; + + private schema?: CfnGraphQLSchema; private mode: SchemaMode; @@ -68,7 +76,7 @@ export class Schema { if (!this.schema) { this.schema = new CfnGraphQLSchema(api, 'Schema', { apiId: api.apiId, - definition: Lazy.stringValue({ produce: () => this.definition }), + definition: Lazy.stringValue({ produce: () => `${this.declareSchema()}${this.definition}` }), }); } return this.schema; @@ -92,6 +100,52 @@ export class Schema { this.definition = `${this.definition}${sep}${addition}\n`; } + /** + * Add a query field to the schema's Query. If one isn't set by + * the user, CDK will create an Object Type called 'Query'. For example, + * + * type Query { + * fieldName: Field.returnType + * } + * + * @param fieldName the name of the query + * @param field the resolvable field to for this query + */ + public addQuery(fieldName: string, field: ResolvableField): ObjectType { + if (this.mode !== SchemaMode.CODE) { + throw new Error(`Unable to add query. Schema definition mode must be ${SchemaMode.CODE} Received: ${this.mode}`); + } + if (!this.query) { + this.query = new ObjectType('Query', { definition: {} }); + this.addType(this.query); + }; + this.query.addField(fieldName, field); + return this.query; + } + + /** + * Add a mutation field to the schema's Mutation. If one isn't set by + * the user, CDK will create an Object Type called 'Mutation'. For example, + * + * type Mutation { + * fieldName: Field.returnType + * } + * + * @param fieldName the name of the Mutation + * @param field the resolvable field to for this Mutation + */ + public addMutation(fieldName: string, field: ResolvableField): ObjectType { + if (this.mode !== SchemaMode.CODE) { + throw new Error(`Unable to add mutation. Schema definition mode must be ${SchemaMode.CODE} Received: ${this.mode}`); + } + if (!this.mutation) { + this.mutation = new ObjectType('Mutation', { definition: {} }); + this.addType(this.mutation); + }; + this.mutation.addField(fieldName, field); + return this.mutation; + } + /** * Add type to the schema * @@ -106,4 +160,27 @@ export class Schema { this.addToSchema(Lazy.stringValue({ produce: () => type.toString() })); return type; } + + /** + * Set the root types of this schema if they are defined. + * + * For example: + * schema { + * query: Query + * mutation: Mutation + * subscription: Subscription + * } + */ + private declareSchema(): string { + if (!this.query && !this.mutation && !this.subscription) { + return ''; + } + type root = 'mutation' | 'query' | 'subscription'; + const list: root[] = ['query', 'mutation', 'subscription']; + return shapeAddition({ + prefix: 'schema', + fields: list.map((key: root) => this[key] ? `${key}: ${this[key]?.name}` : '') + .filter((field) => field != ''), + }) + '\n'; + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-schema.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-schema.test.ts index b229061366117..f60d63f1dec5c 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-schema.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-schema.test.ts @@ -5,9 +5,26 @@ import * as appsync from '../lib'; import * as t from './scalar-type-defintions'; // Schema Definitions -const type = 'type test {\n version: String!\n}\n\n'; -const query = 'type Query {\n getTests: [ test! ]!\n}\n\n'; -const mutation = 'type Mutation {\n addTest(version: String!): test\n}\n'; +const type = new appsync.ObjectType('test', { + definition: { + version: t.required_string, + }, +}); +const query = new appsync.ObjectType('Query', { + definition: { + getTests: new appsync.ResolvableField({ + returnType: type.attribute({ isRequiredList: true, isList: true }), + }), + }, +}); +const mutation = new appsync.ObjectType('Mutation', { + definition: { + addTest: new appsync.ResolvableField({ + returnType: type.attribute(), + args: { version: t.required_string }, + }), + }, +}); let stack: cdk.Stack; beforeEach(() => { @@ -34,13 +51,77 @@ describe('basic testing schema definition mode `code`', () => { const api = new appsync.GraphQLApi(stack, 'API', { name: 'demo', }); - api.addToSchema(type); - api.addToSchema(query); - api.addToSchema(mutation); + api.addType(type); + api.addType(query); + api.addType(mutation); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::GraphQLSchema', { + Definition: `${type.toString()}\n${query.toString()}\n${mutation.toString()}\n`, + }); + }); + + test('definition mode `code` allows for api to addQuery', () => { + // WHEN + const api = new appsync.GraphQLApi(stack, 'API', { + name: 'demo', + }); + api.addQuery('test', new appsync.ResolvableField({ + returnType: t.string, + })); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::GraphQLSchema', { + Definition: 'schema {\n query: Query\n}\ntype Query {\n test: String\n}\n', + }); + }); + + test('definition mode `code` allows for schema to addQuery', () => { + // WHEN + const schema = new appsync.Schema(); + new appsync.GraphQLApi(stack, 'API', { + name: 'demo', + schema, + }); + schema.addQuery('test', new appsync.ResolvableField({ + returnType: t.string, + })); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::GraphQLSchema', { + Definition: 'schema {\n query: Query\n}\ntype Query {\n test: String\n}\n', + }); + }); + + test('definition mode `code` allows for api to addMutation', () => { + // WHEN + const api = new appsync.GraphQLApi(stack, 'API', { + name: 'demo', + }); + api.addMutation('test', new appsync.ResolvableField({ + returnType: t.string, + })); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::GraphQLSchema', { + Definition: 'schema {\n mutation: Mutation\n}\ntype Mutation {\n test: String\n}\n', + }); + }); + + test('definition mode `code` allows for schema to addMutation', () => { + // WHEN + const schema = new appsync.Schema(); + new appsync.GraphQLApi(stack, 'API', { + name: 'demo', + schema, + }); + schema.addMutation('test', new appsync.ResolvableField({ + returnType: t.string, + })); // THEN expect(stack).toHaveResourceLike('AWS::AppSync::GraphQLSchema', { - Definition: `${type}\n${query}\n${mutation}\n`, + Definition: 'schema {\n mutation: Mutation\n}\ntype Mutation {\n test: String\n}\n', }); }); }); @@ -56,11 +137,11 @@ describe('testing schema definition mode `file`', () => { // THEN expect(stack).toHaveResourceLike('AWS::AppSync::GraphQLSchema', { - Definition: `${type}${query}${mutation}`, + Definition: `${type.toString()}\n${query.toString()}\n${mutation.toString()}\n`, }); }); - test('definition mode `file` errors when addObjectType is called', () => { + test('definition mode `file` errors when addType for object is called', () => { // WHEN const api = new appsync.GraphQLApi(stack, 'API', { name: 'demo', @@ -75,7 +156,7 @@ describe('testing schema definition mode `file`', () => { }).toThrowError('API cannot add type because schema definition mode is not configured as CODE.'); }); - test('definition mode `file` errors when addInterfaceType is called', () => { + test('definition mode `file` errors when addType for interface is called', () => { // WHEN const api = new appsync.GraphQLApi(stack, 'API', { name: 'demo', @@ -103,4 +184,29 @@ describe('testing schema definition mode `file`', () => { }).toThrowError('API cannot append to schema because schema definition mode is not configured as CODE.'); }); + test('definition mode `file` errors when addQuery is called', () => { + // WHEN + const api = new appsync.GraphQLApi(stack, 'API', { + name: 'demo', + schema: appsync.Schema.fromAsset(join(__dirname, 'appsync.test.graphql')), + }); + + // THEN + expect(() => { + api.addQuery('blah', new appsync.ResolvableField({ returnType: t.string })); + }).toThrowError('Unable to add query. Schema definition mode must be CODE Received: FILE'); + }); + + test('definition mode `file` errors when addMutation is called', () => { + // WHEN + const api = new appsync.GraphQLApi(stack, 'API', { + name: 'demo', + schema: appsync.Schema.fromAsset(join(__dirname, 'appsync.test.graphql')), + }); + + // THEN + expect(() => { + api.addMutation('blah', new appsync.ResolvableField({ returnType: t.string })); + }).toThrowError('Unable to add mutation. Schema definition mode must be CODE Received: FILE'); + }); }); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/appsync.test.graphql b/packages/@aws-cdk/aws-appsync/test/appsync.test.graphql index b5d531d31fbe9..fa7b3b6a95836 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync.test.graphql +++ b/packages/@aws-cdk/aws-appsync/test/appsync.test.graphql @@ -1,11 +1,9 @@ type test { version: String! } - type Query { - getTests: [ test! ]! + getTests: [test]! } - type Mutation { addTest(version: String!): test } 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 1398b9cbf5382..c8692067ef47a 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 @@ -17,7 +17,7 @@ "ApiId" ] }, - "Definition": "type test {\n version: String!\n}\n\ntype Query {\n getTests: [ test! ]!\n}\n\ntype Mutation {\n addTest(version: String!): test\n}\n" + "Definition": "type test {\n version: String!\n}\ntype Query {\n getTests: [test]!\n}\ntype Mutation {\n addTest(version: String!): test\n}\n" } }, "baseApiDefaultApiKeyA3A0A16A": { 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 0a6fc134d5511..a9ac30ac73a09 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 @@ -16,7 +16,7 @@ "ApiId" ] }, - "Definition": "interface Node {\n created: String\n edited: String\n id: ID!\n}\ntype Planet {\n name: String\n diameter: Int\n rotationPeriod: Int\n orbitalPeriod: Int\n gravity: String\n population: [String]\n climates: [String]\n terrains: [String]\n surfaceWater: Float\n created: String\n edited: String\n id: ID!\n}\ntype Species implements Node {\n name: String\n classification: String\n designation: String\n averageHeight: Float\n averageLifespan: Int\n eyeColors: [String]\n hairColors: [String]\n skinColors: [String]\n language: String\n homeworld: Planet\n created: String\n edited: String\n id: ID!\n}\n\ntype Query {\n getPlanets: [Planet]\n}\n" + "Definition": "schema {\n query: Query\n mutation: Mutation\n}\ninterface Node {\n created: String\n edited: String\n id: ID!\n}\ntype Planet {\n name: String\n diameter: Int\n rotationPeriod: Int\n orbitalPeriod: Int\n gravity: String\n population: [String]\n climates: [String]\n terrains: [String]\n surfaceWater: Float\n created: String\n edited: String\n id: ID!\n}\ntype Species implements Node {\n name: String\n classification: String\n designation: String\n averageHeight: Float\n averageLifespan: Int\n eyeColors: [String]\n hairColors: [String]\n skinColors: [String]\n language: String\n homeworld: Planet\n created: String\n edited: String\n id: ID!\n}\ntype Query {\n getPlanets: [Planet]\n}\ntype Mutation {\n addPlanet(name: String diameter: Int rotationPeriod: Int orbitalPeriod: Int gravity: String population: [String] climates: [String] terrains: [String] surfaceWater: Float): Planet\n}\n" } }, "codefirstapiDefaultApiKey89863A80": { @@ -140,6 +140,27 @@ "codefirstapiSchema148B6CDE" ] }, + "codefirstapiplanetsMutationaddPlanetResolver155AF87E": { + "Type": "AWS::AppSync::Resolver", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "codefirstapi1A3CC7D2", + "ApiId" + ] + }, + "FieldName": "addPlanet", + "TypeName": "Mutation", + "DataSourceName": "planets", + "Kind": "UNIT", + "RequestMappingTemplate": "\n #set($input = $context.arguments)\n $util.qr($input.put(\"name\", $context.arguments.name))\n$util.qr($input.put(\"diameter\", $context.arguments.diameter))\n$util.qr($input.put(\"rotationPeriod\", $context.arguments.rotationPeriod))\n$util.qr($input.put(\"orbitalPeriod\", $context.arguments.orbitalPeriod))\n$util.qr($input.put(\"gravityPeriod\", $context.arguments.gravity))\n$util.qr($input.put(\"population\", $context.arguments.population))\n$util.qr($input.put(\"climates\", $context.arguments.climates))\n$util.qr($input.put(\"terrains\", $context.arguments.terrains))\n$util.qr($input.put(\"surfaceWater\", $context.arguments.surfaceWater))\n {\n \"version\": \"2017-02-28\",\n \"operation\": \"PutItem\",\n \"key\" : {\n \"id\" : $util.dynamodb.toDynamoDBJson($util.autoId())\n },\n \"attributeValues\": $util.dynamodb.toMapValuesJson($input)\n }", + "ResponseMappingTemplate": "$util.toJson($ctx.result)" + }, + "DependsOn": [ + "codefirstapiplanets0F6BB479", + "codefirstapiSchema148B6CDE" + ] + }, "table8235A42E": { "Type": "AWS::DynamoDB::Table", "Properties": { 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 ae2057c68eb64..8bde313c6f724 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.ts +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-schema.ts @@ -34,6 +34,15 @@ const api = new appsync.GraphQLApi(stack, 'code-first-api', { schema: schema, }); +const table = new db.Table(stack, 'table', { + partitionKey: { + name: 'id', + type: db.AttributeType.STRING, + }, +}); + +const tableDS = api.addDynamoDbDataSource('planets', table); + const planet = ObjectType.planet; schema.addToSchema(planet.toString()); @@ -53,22 +62,43 @@ api.addType(new appsync.ObjectType('Species', { }, })); -api.addToSchema('type Query {\n getPlanets: [Planet]\n}', '\n'); - -const table = new db.Table(stack, 'table', { - partitionKey: { - name: 'id', - type: db.AttributeType.STRING, - }, -}); - -const tableDS = api.addDynamoDbDataSource('planets', table); - -tableDS.createResolver({ - typeName: 'Query', - fieldName: 'getPlanets', +api.addQuery('getPlanets', new appsync.ResolvableField({ + returnType: planet.attribute({ isList: true }), + dataSource: tableDS, requestMappingTemplate: appsync.MappingTemplate.dynamoDbScanTable(), responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultList(), -}); +})); + +/* ATTRIBUTES */ +const name = new appsync.Assign('name', '$context.arguments.name'); +const diameter = new appsync.Assign('diameter', '$context.arguments.diameter'); +const rotationPeriod = new appsync.Assign('rotationPeriod', '$context.arguments.rotationPeriod'); +const orbitalPeriod = new appsync.Assign('orbitalPeriod', '$context.arguments.orbitalPeriod'); +const gravity = new appsync.Assign('gravityPeriod', '$context.arguments.gravity'); +const population = new appsync.Assign('population', '$context.arguments.population'); +const climates = new appsync.Assign('climates', '$context.arguments.climates'); +const terrains = new appsync.Assign('terrains', '$context.arguments.terrains'); +const surfaceWater = new appsync.Assign('surfaceWater', '$context.arguments.surfaceWater'); +api.addMutation('addPlanet', new appsync.ResolvableField({ + returnType: planet.attribute(), + args: { + name: ScalarType.string, + diameter: ScalarType.int, + rotationPeriod: ScalarType.int, + orbitalPeriod: ScalarType.int, + gravity: ScalarType.string, + population: ScalarType.list_string, + climates: ScalarType.list_string, + terrains: ScalarType.list_string, + surfaceWater: ScalarType.float, + }, + dataSource: tableDS, + requestMappingTemplate: appsync.MappingTemplate.dynamoDbPutItem( + appsync.PrimaryKey.partition('id').auto(), new appsync.AttributeValues('$context.arguments', + [name, diameter, rotationPeriod, orbitalPeriod, gravity, population, climates, terrains, surfaceWater], + ), + ), + responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultItem(), +})); app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/scalar-type-defintions.ts b/packages/@aws-cdk/aws-appsync/test/scalar-type-defintions.ts index e13df50ca0fc4..80d03d28d1230 100644 --- a/packages/@aws-cdk/aws-appsync/test/scalar-type-defintions.ts +++ b/packages/@aws-cdk/aws-appsync/test/scalar-type-defintions.ts @@ -23,9 +23,8 @@ export const dup_id = GraphqlType.id({ // STRING export const string = GraphqlType.string(); -export const list_string = GraphqlType.string({ - isList: true, -}); +export const required_string = GraphqlType.string({ isRequired: true }); +export const list_string = GraphqlType.string({ isList: true }); // INT export const int = GraphqlType.int(); diff --git a/packages/@aws-cdk/aws-appsync/test/verify.integ.graphql-schema.sh b/packages/@aws-cdk/aws-appsync/test/verify.integ.graphql-schema.sh index 95662531fece5..3270cb0e5606a 100644 --- a/packages/@aws-cdk/aws-appsync/test/verify.integ.graphql-schema.sh +++ b/packages/@aws-cdk/aws-appsync/test/verify.integ.graphql-schema.sh @@ -20,7 +20,13 @@ elif [[ "$1" == "--check" ]]; then usage exit 1 fi - echo THIS TEST SHOULD SUCCEED + echo THIS TEST SHOULD PRODUCE AN EMPTY LIST + curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:$2" -d '{ "query": "query { getPlanets { id name } }" }" }' $3 + echo "" + echo THIS TEST SHOULD RETURN A PLANET OBJECT + curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:$2" -d '{ "query": "mutation ($name: String!) { addPlanet(name: $name) { id name diameter } }", "variables": { "name": "smolPlanet" } }' $3 + echo "" + echo THIS TEST SHOULD PRODUCE AN LIST WITH A SINGLE PLANET curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:$2" -d '{ "query": "query { getPlanets { id name } }" }" }' $3 echo "" elif [[ "$1" == "--clean" ]];then