Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(appsync): code-first schema allows for object type definition #9417

Merged
merged 47 commits into from
Aug 15, 2020
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
46df9f0
complete SchemaBaseType
BryanPan342 Jul 22, 2020
84a7b4c
add doc strings to some classes
BryanPan342 Jul 23, 2020
f3350e3
Merge branch 'master' into appsync-schema
BryanPan342 Jul 23, 2020
ede2020
preliminary changes
BryanPan342 Jul 24, 2020
d9271ec
create enum for schemaDefinition
BryanPan342 Jul 28, 2020
7904e62
adjusted SchemaDefinition to allow for S3
BryanPan342 Jul 28, 2020
4591a70
change addType to addDefinition for temporary inclusion
BryanPan342 Jul 28, 2020
da20dcd
s3 testing
BryanPan342 Jul 28, 2020
6ede81e
Merge branch 'master' into shema
BryanPan342 Aug 3, 2020
4d48a2d
prelim changes
BryanPan342 Aug 3, 2020
63e35b3
prelim run of addType
BryanPan342 Aug 3, 2020
1b82340
reorg schema functions
BryanPan342 Aug 3, 2020
fad768a
== to ===
BryanPan342 Aug 3, 2020
5bec482
chore up schema functions
BryanPan342 Aug 3, 2020
96021db
remove readme section on s3 assets
BryanPan342 Aug 3, 2020
0184c25
remove unneccessary prop this.types
BryanPan342 Aug 3, 2020
2a7c641
remove props
BryanPan342 Aug 3, 2020
076ebdf
expected json file
BryanPan342 Aug 3, 2020
a351a04
listing implementation corrected
BryanPan342 Aug 3, 2020
8637009
update readme
BryanPan342 Aug 3, 2020
0ecd95f
fix bug in integ
BryanPan342 Aug 3, 2020
7005315
pls fix build bug
BryanPan342 Aug 3, 2020
28f7e93
add todo
BryanPan342 Aug 3, 2020
fc98566
dont change list or required
BryanPan342 Aug 4, 2020
c27a158
prelim testing for the OO design
BryanPan342 Aug 5, 2020
72d8be4
new implementation
BryanPan342 Aug 6, 2020
bff3e3b
oops.. forgot to stage changes
BryanPan342 Aug 6, 2020
47d9c89
revert changes to prevent merge conflicts
BryanPan342 Aug 6, 2020
1e2dd60
updated api for generating attribute types
BryanPan342 Aug 7, 2020
d8ee61c
Merge branch 'master' into shema
BryanPan342 Aug 8, 2020
1d1aec9
round off integ test, clean up writing, change api name
BryanPan342 Aug 10, 2020
81563c0
Merge branch 'shema' of https://github.com/BryanPan342/aws-cdk into s…
BryanPan342 Aug 10, 2020
a1e34a3
chore up readme
BryanPan342 Aug 10, 2020
e7b57a4
chore up unit tests
BryanPan342 Aug 10, 2020
7c8115f
allow for object types to be created externally
BryanPan342 Aug 10, 2020
04705e4
update integ for external object type
BryanPan342 Aug 10, 2020
bd6868c
prevent faiils
BryanPan342 Aug 10, 2020
050b160
clean up code and finish up readme for object types
BryanPan342 Aug 10, 2020
08aa720
style change to readme
BryanPan342 Aug 10, 2020
d5bd67c
interfaces implemented
BryanPan342 Aug 11, 2020
315ffbf
clean up some implementation and some docs
BryanPan342 Aug 13, 2020
de4c2ad
edit schema
BryanPan342 Aug 13, 2020
2dc962b
make sure implement interface is appended
BryanPan342 Aug 13, 2020
912a674
bug fix
BryanPan342 Aug 14, 2020
4880a53
Merge branch 'master' into shema
BryanPan342 Aug 15, 2020
5337c5a
fix merge conflicts
BryanPan342 Aug 15, 2020
1da6d11
merge conflicts
BryanPan342 Aug 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 185 additions & 1 deletion packages/@aws-cdk/aws-appsync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const api = new appsync.GraphQLApi(stack, 'Api', {
const demoTable = new db.Table(stack, 'DemoTable', {
partitionKey: {
name: 'id',
type: AttributeType.STRING,
type: db.AttributeType.STRING,
},
});

Expand Down Expand Up @@ -161,3 +161,187 @@ api.grantMutation(role, 'updateExample');
// For custom types and granular design
api.grant(role, appsync.IamResource.ofType('Mutation', 'updateExample'), 'appsync:GraphQL');
```

### 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:
- **modularity**: organizing schema type definitions into different files
- **reusability**: simplifying down boilerplate/repetitive code
- **consistency**: resolvers and schema definition will always be synced

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

We are going to reference the [example](#Example) through a code-first approach.

```ts
import * as appsync from '@aws-cdk/aws-appsync';
import * as db from '@aws-cdk/aws-dynamodb';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import * as db from '@aws-cdk/aws-dynamodb';
import * as ddb from '@aws-cdk/aws-dynamodb';


const api = new appsync.GraphQLApi(stack, 'Api', {
name: 'demo',
schemaDefinition: appsync.SchemaDefinition.FILE,
schemaDefinitionFile: join(__dirname, 'schema.graphql'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might want to provide an example that includes that this file contains

authorizationConfig: {
defaultAuthorization: {
authorizationType: appsync.AuthorizationType.IAM
},
},
});

const demoTable = new db.Table(stack, 'DemoTable', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const demoTable = new db.Table(stack, 'DemoTable', {
const demoTable = new ddb.Table(stack, 'DemoTable', {

partitionKey: {
name: 'id',
type: db.AttributeType.STRING,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type: db.AttributeType.STRING,
type: ddb.AttributeType.STRING,

},
});

const demoDS = api.addDynamoDbDataSource('demoDataSource', 'Table for Demos', demoTable);

// Schema Definition starts here

const demo = api.addType('demo', {
definition: {
id: appsync.GraphqlType.string({ isRequired: true }),
version: appsync.GraphqlType.string({ isRequired: true }),
},
});

```

#### 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**.

GraphQL Types are the building blocks of types, whether they are scalar, objects,
interfaces, etc. GraphQL Types can be:
- [**Scalar Types**](https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html): Id, Int, String, AWSDate, etc.
- **Object Types**: types that you generate (i.e. `demo` from the example above)
- **Interface Types**: abstract types that define the base implementation of other
Intermediate Types

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`.

#### Intermediate Types

Intermediate Types are abstractions above Scalar Types. They have a set of defined
fields, where each field corresponds to another type in the system. Intermediate
Types will be the meat of your GraphQL Schema as they are the types defined by you.

Intermediate Types include:
- [**Interface Types**](#Interface-Types)
- [**Object Types**](#Object-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
to generate Object Types with less work.

You can create Interface Types in two ways:

1. Interface Types can be created ***externally***.
```ts
const node = new appsync.InterfaceType('Node', {
definition: {
id: appsync.GraphqlType.string({ isRequired: true }),
},
});
```
2. Interface Types can be created ***externally*** from another Interface Type.
```ts
const node = new appsync.InterfaceType('Node', {
definition: {
id: appsync.GraphqlType.string({ isRequired: true }),
},
});
const superNode = new appsync.InterfaceType.extendInterface('SuperNode', node, {
definition: {
speicalId: appsync.GraphqlType.string(),
},
});
```

#### 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
GraphQL Types and are only usable when linked to a GraphQL Api.

You can create Object Types in three ways:

1. Object Types can be created ***externally***.
```ts
const api = new appsync.GraphQLApi(stack, 'Api', {
name: 'demo',
schemaDefinition: appsync.SchemaDefinition.CODE,
});
const demo = new appsync.ObjectType('Demo', {
defintion: {
id: appsync.GraphqlType.string({ isRequired: true }),
version: appsync.GraphqlType.string({ isRequired: true }),
},
});

api.appendToSchema(object.toString());
```
> This method allows for reusability and modularity, ideal for larger projects.
For example, imagine moving all Object Type definition outside the stack.

`scalar-types.ts` - a file for scalar type definitions
```ts
export const required_string = new appsync.GraphqlType.string({ isRequired: true });
```

`object-types.ts` - a file for object type definitions
```ts
import { required_string } from './scalar-types';
export const demo = new appsync.ObjectType('Demo', {
defintion: {
id: required_string,
version: required_string,
},
});
```

`cdk-stack.ts` - a file containing our cdk stack
```ts
import { demo } from './object-types';
api.appendToSchema(demo.toString());
```

2. Object Types can be created ***externally*** from an Interface Type.
```ts
const node = new appsync.InterfaceType('Node', {
definition: {
id: appsync.GraphqlType.string({ isRequired: true }),
},
});
const demo = new appsync.ObjectType.implementInterface('Demo', node, {
defintion: {
version: appsync.GraphqlType.string({ isRequired: true }),
},
});
```
> 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.

60 changes: 43 additions & 17 deletions packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IFunction } from '@aws-cdk/aws-lambda';
import { Construct, Duration, IResolvable, Stack } from '@aws-cdk/core';
import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema } from './appsync.generated';
import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource } from './data-source';
import { ObjectType, ObjectTypeProps } from './schema-types';

/**
* enum with all possible values for AppSync authorization type
Expand Down Expand Up @@ -683,33 +684,20 @@ export class GraphQLApi extends Construct {
return authModes ? this.formatAdditionalAuthorizationModes(authModes) : undefined;
}

/**
* Sets schema defintiion to input if schema mode is configured with SchemaDefinition.CODE
*
* @param definition string that is the graphql representation of schema
* @experimental temporary
*/
public updateDefinition (definition: string): void{
if ( this.schemaMode != SchemaDefinition.CODE ) {
throw new Error('API cannot add type because schema definition mode is not configured as CODE.');
}
this.schema.definition = definition;
}

/**
* Define schema based on props configuration
* @param file the file name/s3 location of Schema
*/
private defineSchema(file?: string): CfnGraphQLSchema {
let definition;

if ( this.schemaMode == SchemaDefinition.FILE && !file) {
if ( this.schemaMode === SchemaDefinition.FILE && !file) {
throw new Error('schemaDefinitionFile must be configured if using FILE definition mode.');
} else if ( this.schemaMode == SchemaDefinition.FILE && file ) {
} else if ( this.schemaMode === SchemaDefinition.FILE && file ) {
definition = readFileSync(file).toString('UTF-8');
} else if ( this.schemaMode == SchemaDefinition.CODE && !file ) {
} else if ( this.schemaMode === SchemaDefinition.CODE && !file ) {
definition = '';
} else if ( this.schemaMode == SchemaDefinition.CODE && file) {
} else if ( this.schemaMode === SchemaDefinition.CODE && file) {
throw new Error('definition mode CODE is incompatible with file definition. Change mode to FILE/S3 or unconfigure schemaDefinitionFile');
}

Expand All @@ -718,4 +706,42 @@ export class GraphQLApi extends Construct {
definition,
});
}

/**
* Escape hatch to append to Schema as desired. Will always result
* in a newline.
*
* @param addition the addition to add to schema
* @param delimiter the delimiter between schema and addition
* @default - ''
*
* @experimental
*/
public appendToSchema(addition: string, delimiter?: string): void {
if ( this.schemaMode != SchemaDefinition.CODE ) {
throw new Error('API cannot append to schema because schema definition mode is not configured as CODE.');
}
const sep = delimiter ?? '';
this.schema.definition = `${this.schema.definition}${sep}${addition}\n`;
}

/**
* Add an object type to the schema
*
* @param name the name of the object type
* @param props the definition
*
* @experimental
*/
public addType(name: string, props: ObjectTypeProps): ObjectType {
if ( this.schemaMode != SchemaDefinition.CODE ) {
throw new Error('API cannot add type because schema definition mode is not configured as CODE.');
};
const type = new ObjectType(name, {
definition: props.definition,
directives: props.directives,
});
this.appendToSchema(type.toString());
return type;
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-appsync/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export * from './key';
export * from './data-source';
export * from './mapping-template';
export * from './resolver';
export * from './schema-types';
export * from './graphqlapi';
Loading