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): separating schema from graphql api #9903

Merged
merged 89 commits into from
Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 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
14093f6
implemeneted resolvable fields
BryanPan342 Aug 13, 2020
315ffbf
clean up some implementation and some docs
BryanPan342 Aug 13, 2020
66b3685
Merge branch 'shema' into resolvable-fields
BryanPan342 Aug 13, 2020
de4c2ad
edit schema
BryanPan342 Aug 13, 2020
2dc962b
make sure implement interface is appended
BryanPan342 Aug 13, 2020
dfbe57d
Merge branch 'shema' into resolvable-fields
BryanPan342 Aug 14, 2020
912a674
bug fix
BryanPan342 Aug 14, 2020
8bf6ef5
Merge branch 'shema' into resolvable-fields
BryanPan342 Aug 14, 2020
6511d86
fix a bug in implementation
BryanPan342 Aug 14, 2020
beee254
update code-first example on readme
BryanPan342 Aug 14, 2020
dac080d
Merge branch 'master' into resolvable-fields
BryanPan342 Aug 15, 2020
a45ab5c
make resolvable fields as part of graphql type
BryanPan342 Aug 17, 2020
9a6fb99
separate and create IField Interface
BryanPan342 Aug 17, 2020
72cad42
write test cases
BryanPan342 Aug 17, 2020
e0bb9c0
remove unneccessary static function
BryanPan342 Aug 18, 2020
bf1133c
Merge branch 'master' into resolvable-fields
BryanPan342 Aug 18, 2020
3478a47
first pass changes
BryanPan342 Aug 18, 2020
1c549b5
write tests and fix bugs
BryanPan342 Aug 18, 2020
988d2da
remove unneccessary function
BryanPan342 Aug 18, 2020
2f42e64
bug fix
BryanPan342 Aug 18, 2020
f938da8
update test
BryanPan342 Aug 18, 2020
6eb781f
update Readme and make interface for intermediate type
BryanPan342 Aug 19, 2020
2d89379
remove spacing
BryanPan342 Aug 19, 2020
1100f1f
add links
BryanPan342 Aug 19, 2020
57fcb03
address issue
BryanPan342 Aug 20, 2020
783415c
Merge branch 'master' into resolvable-fields
BryanPan342 Aug 20, 2020
11d0f3d
lint
BryanPan342 Aug 20, 2020
b804c15
separating schema from graphql api
BryanPan342 Aug 21, 2020
d0fddda
update readme
BryanPan342 Aug 21, 2020
3d3fa2b
fix suggestions
BryanPan342 Aug 21, 2020
cb6a21b
Merge branch 'master' into resolvable-fields
BryanPan342 Aug 21, 2020
911c87d
implement pipelineConfig + refactor
BryanPan342 Aug 24, 2020
f4df2aa
Merge branch 'master' into schema
BryanPan342 Aug 24, 2020
c23029f
update readme
BryanPan342 Aug 24, 2020
8d510fb
api changes
BryanPan342 Aug 24, 2020
abd1057
fix readme
BryanPan342 Aug 24, 2020
c0e4642
Merge branch 'resolvable-fields' into schema
BryanPan342 Aug 24, 2020
c8408e0
add tests
BryanPan342 Aug 24, 2020
0fd0f2c
allow public definition
BryanPan342 Aug 24, 2020
ba9f337
Merge branch 'master' into schema
BryanPan342 Aug 24, 2020
7ffe7c7
address changes
BryanPan342 Aug 25, 2020
70d584e
update readme
BryanPan342 Aug 25, 2020
8bb0a2a
address suggestions
BryanPan342 Aug 25, 2020
f08f4b8
Merge branch 'master' into schema
BryanPan342 Aug 25, 2020
b3f6843
jsii bug
BryanPan342 Aug 25, 2020
b871a2a
remove addXxxType
BryanPan342 Aug 26, 2020
6f4f5f6
Merge branch 'master' into schema
BryanPan342 Aug 26, 2020
b24be5e
Merge branch 'master' into schema
BryanPan342 Aug 26, 2020
4119220
Merge branch 'master' into schema
BryanPan342 Aug 26, 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
73 changes: 57 additions & 16 deletions packages/@aws-cdk/aws-appsync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ import * as db from '@aws-cdk/aws-dynamodb';

const api = new appsync.GraphQLApi(stack, 'Api', {
name: 'demo',
schemaDefinition: appsync.SchemaDefinition.FILE,
schemaDefinitionFile: join(__dirname, 'schema.graphql'),
schema: appsync.Schema.fromAsset(join(__dirname, 'schema.graphql')),
authorizationConfig: {
defaultAuthorization: {
authorizationType: appsync.AuthorizationType.IAM
Expand Down Expand Up @@ -83,7 +82,53 @@ demoDS.createResolver({
});
```

## Imports
### Schema

Every GraphQL Api needs a schema to define the Api. CDK offers `appsync.Schema`
for static convenience methods for various types of schema declaration: code-first
or schema-first.

#### Code-First

When declaring your GraphQL Api, CDK defaults to a code-first approach if the
`schema` property is not configured.

```ts
const api = new appsync.GraphQLApi(stack, 'api', { name: 'myApi' });
```

CDK will declare a `Schema` class that will give your Api access functions to
define your schema code-first: `addType`, `addObjectType`, `addToSchema`, etc.

You can also declare your `Schema` class outside of your CDK stack, to define
your schema externally.

```ts
const schema = new appsync.Schema();
schema.addObjectType('demo', {
definition: { id: appsync.GraphqlType.id() },
});
const api = new appsync.GraphQLApi(stack, 'api', {
name: 'myApi',
schema
});
```

See the [code-first schema](#Code-First-Schema) section for more details.

#### Schema-First

You can define your GraphQL Schema from a file on disk. For convenience, use
the `appsync.Schema.fromAsset` to specify the file representing your schema.

```ts
const api = appsync.GraphQLApi(stack, 'api', {
BryanPan342 marked this conversation as resolved.
Show resolved Hide resolved
name: 'myApi',
schema: appsync.Schema.fromAsset(join(__dirname, 'schema.graphl')),
});
```

### Imports

Any GraphQL Api that has been created outside the stack can be imported from
another stack into your CDK app. Utilizing the `fromXxx` function, you have
Expand All @@ -101,7 +146,7 @@ If you don't specify `graphqlArn` in `fromXxxAttributes`, CDK will autogenerate
the expected `arn` for the imported api, given the `apiId`. For creating data
sources and resolvers, an `apiId` is sufficient.

## Permissions
### Permissions

When using `AWS_IAM` as the authorization type for GraphQL API, an IAM Role
with correct permissions must be used for access to API.
Expand Down Expand Up @@ -153,7 +198,7 @@ const api = new appsync.GraphQLApi(stack, 'API', {
api.grant(role, appsync.IamResource.custom('types/Mutation/fields/updateExample'), 'appsync:GraphQL')
```

### IamResource
#### IamResource

In order to use the `grant` functions, you need to use the class `IamResource`.

Expand All @@ -163,7 +208,7 @@ In order to use the `grant` functions, you need to use the class `IamResource`.

- `IamResource.all()` permits ALL resources.

### Generic Permissions
#### Generic Permissions

Alternatively, you can use more generic `grant` functions to accomplish the same usage.

Expand Down Expand Up @@ -280,7 +325,6 @@ import * as schema from './object-types';

const api = new appsync.GraphQLApi(stack, 'Api', {
name: 'demo',
schemaDefinition: appsync.SchemaDefinition.CODE,
});

this.objectTypes = [ schema.Node, schema.Film ];
Expand All @@ -294,13 +338,12 @@ api.addType('Query', {
args: schema.args,
requestMappingTemplate: dummyRequest,
responseMappingTemplate: dummyResponse,
},
}),
}
});
})
});

this.objectTypes.map((t) => api.appendToSchema(t));
Object.keys(filmConnections).forEach((key) => api.appendToSchema(filmConnections[key]));
this.objectTypes.map((t) => api.addType(t));
Object.keys(filmConnections).forEach((key) => api.addType(filmConnections[key]));
```

Notice how we can utilize the `generateEdgeAndConnection` function to generate
Expand Down Expand Up @@ -457,7 +500,6 @@ You can create Object Types in three ways:
```ts
const api = new appsync.GraphQLApi(stack, 'Api', {
name: 'demo',
schemaDefinition: appsync.SchemaDefinition.CODE,
});
const demo = new appsync.ObjectType('Demo', {
defintion: {
Expand All @@ -466,7 +508,7 @@ You can create Object Types in three ways:
},
});

api.appendToSchema(object.toString());
api.addType(object);
```
> This method allows for reusability and modularity, ideal for larger projects.
For example, imagine moving all Object Type definition outside the stack.
Expand All @@ -490,7 +532,7 @@ You can create Object Types in three ways:
`cdk-stack.ts` - a file containing our cdk stack
```ts
import { demo } from './object-types';
api.appendToSchema(demo.toString());
api.addType(demo);
```

2. Object Types can be created ***externally*** from an Interface Type.
Expand All @@ -513,7 +555,6 @@ You can create Object Types in three ways:
```ts
const api = new appsync.GraphQLApi(stack, 'Api', {
name: 'demo',
schemaDefinition: appsync.SchemaDefinition.CODE,
});
api.addType('Demo', {
defintion: {
Expand Down
100 changes: 22 additions & 78 deletions packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { readFileSync } from 'fs';
import { IUserPool } from '@aws-cdk/aws-cognito';
import { ManagedPolicy, Role, ServicePrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam';
import { CfnResource, Construct, Duration, IResolvable, Stack } from '@aws-cdk/core';
import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema } from './appsync.generated';
import { IGraphqlApi, GraphqlApiBase } from './graphqlapi-base';
import { ObjectType, ObjectTypeProps } from './schema-intermediate';
import { Schema } from './schema';
import { IIntermediateType } from './schema-base';

/**
* enum with all possible values for AppSync authorization type
Expand Down Expand Up @@ -201,21 +201,6 @@ export interface LogConfig {
readonly fieldLogLevel?: FieldLogLevel;
}

/**
* Enum containing the different modes of schema definition
*/
export enum SchemaDefinition {
/**
* Define schema through functions like addType, addQuery, etc.
*/
CODE = 'CODE',

/**
* Define schema in a file, i.e. schema.graphql
*/
FILE = 'FILE',
}

/**
* Properties for an AppSync GraphQL API
*/
Expand All @@ -242,18 +227,13 @@ export interface GraphQLApiProps {
/**
* GraphQL schema definition. Specify how you want to define your schema.
*
* SchemaDefinition.CODE allows schema definition through CDK
* SchemaDefinition.FILE allows schema definition through schema.graphql file
* Schema.fromFile(filePath: string) allows schema definition through schema.graphql file
*
* @experimental
*/
readonly schemaDefinition: SchemaDefinition;
/**
* File containing the GraphQL schema definition. You have to specify a definition or a file containing one.
* @default - schema will be generated code-first (i.e. addType, addObjectType, etc.)
*
* @default - Use schemaDefinition
* @experimental
*/
readonly schemaDefinitionFile?: string;
readonly schema?: Schema;
/**
* A flag indicating whether or not X-Ray tracing is enabled for the GraphQL API.
*
Expand Down Expand Up @@ -390,9 +370,9 @@ export class GraphQLApi extends GraphqlApiBase {
public readonly name: string;

/**
* underlying CFN schema resource
* the schema attached to this api
*/
public readonly schema: CfnGraphQLSchema;
public readonly schema: Schema;

/**
* the configured API key, if present
Expand All @@ -401,9 +381,9 @@ export class GraphQLApi extends GraphqlApiBase {
*/
public readonly apiKey?: string;

private schemaMode: SchemaDefinition;
private schemaResource: CfnGraphQLSchema;
private api: CfnGraphQLApi;
private _apiKey?: CfnApiKey;
private apiKeyResource?: CfnApiKey;

constructor(scope: Construct, id: string, props: GraphQLApiProps) {
super(scope, id);
Expand All @@ -429,16 +409,16 @@ export class GraphQLApi extends GraphqlApiBase {
this.arn = this.api.attrArn;
this.graphQlUrl = this.api.attrGraphQlUrl;
this.name = this.api.name;
this.schemaMode = props.schemaDefinition;
this.schema = this.defineSchema(props.schemaDefinitionFile);
this.schema = props.schema ?? new Schema();
this.schemaResource = this.schema.bind(this);

if (modes.some((mode) => mode.authorizationType === AuthorizationType.API_KEY)) {
const config = modes.find((mode: AuthorizationMode) => {
return mode.authorizationType === AuthorizationType.API_KEY && mode.apiKeyConfig;
})?.apiKeyConfig;
this._apiKey = this.createAPIKey(config);
this._apiKey.addDependsOn(this.schema);
this.apiKey = this._apiKey.attrApiKey;
this.apiKeyResource = this.createAPIKey(config);
this.apiKeyResource.addDependsOn(this.schemaResource);
this.apiKey = this.apiKeyResource.attrApiKey;
}
}

Expand Down Expand Up @@ -515,7 +495,7 @@ export class GraphQLApi extends GraphqlApiBase {
* @param construct the dependee
*/
public addSchemaDependency(construct: CfnResource): boolean {
construct.addDependsOn(this.schema);
construct.addDependsOn(this.schemaResource);
return true;
}

Expand Down Expand Up @@ -584,29 +564,6 @@ export class GraphQLApi extends GraphqlApiBase {
});
}

/**
* 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) {
throw new Error('schemaDefinitionFile must be configured if using FILE definition mode.');
} else if ( this.schemaMode === SchemaDefinition.FILE && file ) {
definition = readFileSync(file).toString('utf-8');
} else if ( this.schemaMode === SchemaDefinition.CODE && !file ) {
definition = '';
} 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');
}

return new CfnGraphQLSchema(this, 'Schema', {
apiId: this.apiId,
definition,
});
}

/**
* Escape hatch to append to Schema as desired. Will always result
* in a newline.
Expand All @@ -617,31 +574,18 @@ export class GraphQLApi extends GraphqlApiBase {
*
* @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`;
public addToSchema(addition: string, delimiter?: string): void {
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: I wonder if we need to surface this here, we could probably just allow users to call api.schema.addToSchema if they need the escape hatch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea.. might be misleading to other schema-first users as well...

Ill remove it and the addObjectType and addInterfaceType in the following PR :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually nvm the queue to merge is fat.. ill remove it rn lmao

this.schema.addToSchema(addition, delimiter);
}

/**
* Add an object type to the schema
* Add type to the schema
*
* @param name the name of the object type
* @param props the definition
* @param type the intermediate type to add to the schema
*
* @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;
public addType(type: IIntermediateType): IIntermediateType {
return this.schema.addType(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,6 +4,7 @@ export * from './key';
export * from './data-source';
export * from './mapping-template';
export * from './resolver';
export * from './schema';
export * from './schema-intermediate';
export * from './schema-field';
export * from './schema-base';
Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-appsync/lib/private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ function concatAndDedup<T>(left: T[], right: T[]): T[] {
});
}

/**
* Utility enum for Schema class
*/
export enum SchemaMode {
FILE = 'FILE',
CODE = 'CODE',
};

/**
* Utility class to represent DynamoDB key conditions.
*/
Expand Down
12 changes: 11 additions & 1 deletion packages/@aws-cdk/aws-appsync/lib/schema-base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Resolver } from './resolver';
import { ResolvableFieldOptions } from './schema-field';
import { ResolvableFieldOptions, BaseTypeOptions, GraphqlType } from './schema-field';
import { InterfaceType } from './schema-intermediate';

/**
Expand Down Expand Up @@ -105,6 +105,16 @@ export interface IIntermediateType {
*/
readonly intermediateType?: InterfaceType;

/**
* Create an GraphQL Type representing this Intermediate Type
*
* @param options the options to configure this attribute
* - isList
* - isRequired
* - isRequiredList
*/
attribute(options?: BaseTypeOptions): GraphqlType;

/**
* Generate the string of this object type
*/
Expand Down
Loading