Skip to content

Commit

Permalink
feat(appsync): import existing graphql api (#9254)
Browse files Browse the repository at this point in the history
**[ISSUE]**
Appsync missing `fromXxx` functionality, making multiple stack appsync interaction impossible. 

**[Approach]**
Created a base class for  `GraphQLApi` and a `IGraphQLApi` interface for imports. Added `fromXxxAttributes` functions to code base that can add dataSources.

**[Notes]**
Only accessible props from `IGraphQLApi` are `apiId` and `arn`. Only accessible functions from `IGraphQLApi` are the `addXxxDataSource` functions.

Added `props-physical-name:@aws-cdk/aws-appsync.GraphQLApiProps` as linter exception because the breaking change isn't worth the return of making the physical name (name exists already).

Added `from-method:@aws-cdk/aws-appsync.GraphQLApi` as linter exception because a `fromGraphQLApiAttributes` function will turn into `from_graph_q_l_api_attributes` in python.

Fixes #6959 

BREAKING CHANGE: **appsync.addXxxDataSource** `name` and `description` props are now optional and in an `DataSourceOptions` interface. 
- **appsync**:  the props `name` and `description` in `addXxxDataSource` have been moved into new props `options`  of type `DataSourceOptions`
- **appsync**: `DataSourceOptions.name` defaults to id
- **appsync**: `DataSourceOptions.description` defaults to undefined

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
BryanPan342 committed Aug 15, 2020
1 parent 1893a5e commit 5732b8e
Show file tree
Hide file tree
Showing 18 changed files with 1,099 additions and 182 deletions.
20 changes: 19 additions & 1 deletion packages/@aws-cdk/aws-appsync/README.md
Expand Up @@ -64,7 +64,7 @@ const demoTable = new db.Table(stack, 'DemoTable', {
},
});

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

// Resolver for the Query "getDemos" that scans the DyanmoDb table and returns the entire list.
demoDS.createResolver({
Expand All @@ -83,6 +83,24 @@ demoDS.createResolver({
});
```

## 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
the ability to add data sources and resolvers through a `IGraphQLApi` interface.

```ts
const importedApi = appsync.GraphQLApi.fromGraphQLApiAttributes(stack, 'IApi', {
graphqlApiId: api.apiId,
graphqlArn: api.arn,
});
importedApi.addDynamoDbDataSource('TableDataSource', table);
```

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

When using `AWS_IAM` as the authorization type for GraphQL API, an IAM Role
Expand Down
16 changes: 9 additions & 7 deletions packages/@aws-cdk/aws-appsync/lib/data-source.ts
Expand Up @@ -3,7 +3,7 @@ import { IGrantable, IPrincipal, IRole, Role, ServicePrincipal } from '@aws-cdk/
import { IFunction } from '@aws-cdk/aws-lambda';
import { Construct, IResolvable } from '@aws-cdk/core';
import { CfnDataSource } from './appsync.generated';
import { GraphQLApi } from './graphqlapi';
import { IGraphqlApi } from './graphqlapi-base';
import { BaseResolverProps, Resolver } from './resolver';

/**
Expand All @@ -13,11 +13,13 @@ export interface BaseDataSourceProps {
/**
* The API to attach this data source to
*/
readonly api: GraphQLApi;
readonly api: IGraphqlApi;
/**
* The name of the data source
*
* @default - id of data source
*/
readonly name: string;
readonly name?: string;
/**
* the description of the data source
*
Expand Down Expand Up @@ -91,7 +93,7 @@ export abstract class BaseDataSource extends Construct {
*/
public readonly ds: CfnDataSource;

protected api: GraphQLApi;
protected api: IGraphqlApi;
protected serviceRole?: IRole;

constructor(scope: Construct, id: string, props: BackedDataSourceProps, extended: ExtendedDataSourceProps) {
Expand All @@ -100,15 +102,15 @@ export abstract class BaseDataSource extends Construct {
if (extended.type !== 'NONE') {
this.serviceRole = props.serviceRole || new Role(this, 'ServiceRole', { assumedBy: new ServicePrincipal('appsync') });
}

const name = props.name ?? id;
this.ds = new CfnDataSource(this, 'Resource', {
apiId: props.api.apiId,
name: props.name,
name: name,
description: props.description,
serviceRoleArn: this.serviceRole?.roleArn,
...extended,
});
this.name = props.name;
this.name = name;
this.api = props.api;
}

Expand Down
177 changes: 177 additions & 0 deletions packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts
@@ -0,0 +1,177 @@
import { ITable } from '@aws-cdk/aws-dynamodb';
import { IFunction } from '@aws-cdk/aws-lambda';
import { CfnResource, IResource, Resource } from '@aws-cdk/core';
import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource } from './data-source';

/**
* Optional configuration for data sources
*/
export interface DataSourceOptions {
/**
* The name of the data source, overrides the id given by cdk
*
* @default - generated by cdk given the id
*/
readonly name?: string;

/**
* The description of the data source
*
* @default - No description
*/
readonly description?: string;
}

/**
* Interface for GraphQL
*/
export interface IGraphqlApi extends IResource {

/**
* an unique AWS AppSync GraphQL API identifier
* i.e. 'lxz775lwdrgcndgz3nurvac7oa'
*
* @attribute
*/
readonly apiId: string;

/**
* the ARN of the API
*
* @attribute
*/
readonly arn: string;

/**
* add a new dummy data source to this API. Useful for pipeline resolvers
* and for backend changes that don't require a data source.
*
* @param id The data source's id
* @param options The optional configuration for this data source
*/
addNoneDataSource(id: string, options?: DataSourceOptions): NoneDataSource;

/**
* add a new DynamoDB data source to this API
*
* @param id The data source's id
* @param table The DynamoDB table backing this data source
* @param options The optional configuration for this data source
*/
addDynamoDbDataSource(id: string, table: ITable, options?: DataSourceOptions): DynamoDbDataSource;

/**
* add a new http data source to this API
*
* @param id The data source's id
* @param endpoint The http endpoint
* @param options The optional configuration for this data source
*/
addHttpDataSource(id: string, endpoint: string, options?: DataSourceOptions): HttpDataSource;

/**
* add a new Lambda data source to this API
*
* @param id The data source's id
* @param lambdaFunction The Lambda function to call to interact with this data source
* @param options The optional configuration for this data source
*/
addLambdaDataSource(id: string, lambdaFunction: IFunction, options?: DataSourceOptions): LambdaDataSource;

/**
* Add schema dependency if not imported
*
* @param construct the dependee
*/
addSchemaDependency(construct: CfnResource): boolean;
}

/**
* Base Class for GraphQL API
*/
export abstract class GraphqlApiBase extends Resource implements IGraphqlApi {

/**
* an unique AWS AppSync GraphQL API identifier
* i.e. 'lxz775lwdrgcndgz3nurvac7oa'
*/
public abstract readonly apiId: string;

/**
* the ARN of the API
*/
public abstract readonly arn: string;

/**
* add a new dummy data source to this API. Useful for pipeline resolvers
* and for backend changes that don't require a data source.
*
* @param id The data source's id
* @param options The optional configuration for this data source
*/
public addNoneDataSource(id: string, options?: DataSourceOptions): NoneDataSource {
return new NoneDataSource(this, id, {
api: this,
name: options?.name,
description: options?.description,
});
}

/**
* add a new DynamoDB data source to this API
*
* @param id The data source's id
* @param table The DynamoDB table backing this data source
* @param options The optional configuration for this data source
*/
public addDynamoDbDataSource(id: string, table: ITable, options?: DataSourceOptions): DynamoDbDataSource {
return new DynamoDbDataSource(this, id, {
api: this,
table,
name: options?.name,
description: options?.description,
});
}

/**
* add a new http data source to this API
*
* @param id The data source's id
* @param endpoint The http endpoint
* @param options The optional configuration for this data source
*/
public addHttpDataSource(id: string, endpoint: string, options?: DataSourceOptions): HttpDataSource {
return new HttpDataSource(this, id, {
api: this,
endpoint,
name: options?.name,
description: options?.description,
});
}

/**
* add a new Lambda data source to this API
*
* @param id The data source's id
* @param lambdaFunction The Lambda function to call to interact with this data source
* @param options The optional configuration for this data source
*/
public addLambdaDataSource(id: string, lambdaFunction: IFunction, options?: DataSourceOptions): LambdaDataSource {
return new LambdaDataSource(this, id, {
api: this,
lambdaFunction,
name: options?.name,
description: options?.description,
});
}

/**
* Add schema dependency if not imported
*
* @param construct the dependee
*/
public addSchemaDependency(construct: CfnResource): boolean {
construct;
return false;
}
}

0 comments on commit 5732b8e

Please sign in to comment.