Skip to content

Commit

Permalink
feat(api): add arrays and objects support for rds datasource
Browse files Browse the repository at this point in the history
  • Loading branch information
sundersc committed Aug 1, 2023
1 parent d9afbe5 commit cbfb017
Show file tree
Hide file tree
Showing 10 changed files with 583 additions and 32 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1558,4 +1558,38 @@ describe('ModelTransformer: ', () => {
validateModelSchema(parse(out.schema));
parse(out.schema);
});

it('should successfully transform rds schema with array and object fields', async () => {
const validSchema = `
type Note @model {
id: ID!
content: String!
tags: [String!]
attachments: Attachment
}
type Attachment {
report: String!
image: String!
}
`;

const transformer = new GraphQLTransform({
transformers: [new ModelTransformer()],
});
const modelToDatasourceMap = new Map<string, DatasourceType>();
modelToDatasourceMap.set('Note', {
dbType: 'MySQL',
provisionDB: false,
});
const out = transformer.transform(validSchema, {
modelToDatasourceMap,
});
expect(out).toBeDefined();

validateModelSchema(parse(out.schema));
parse(out.schema);
expect(out.schema).toMatchSnapshot();
expect(out.resolvers).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
import {
generateUpdateRequestTemplate,
generateCreateRequestTemplate,
Expand All @@ -22,34 +23,35 @@ import {
} from './vtl-generator';

export class DynamoDBModelVTLGenerator implements ModelVTLGenerator {
generateUpdateRequestTemplate(config: ModelUpdateRequestConfig): string {
generateUpdateRequestTemplate(config: ModelUpdateRequestConfig, ctx: TransformerContextProvider): string {
return generateUpdateRequestTemplate(config.modelName, config.isSyncEnabled);
}

generateCreateRequestTemplate(config: ModelCreateRequestConfig): string {
generateCreateRequestTemplate(config: ModelCreateRequestConfig, ctx: TransformerContextProvider): string {
return generateCreateRequestTemplate(config.modelName, config.modelIndexFields);
}

generateCreateInitSlotTemplate(config: ModelCreateInitSlotConfig, initializeIdField: boolean): string {
return generateCreateInitSlotTemplate(config.modelConfig, initializeIdField);
}

generateDeleteRequestTemplate(config: ModelUpdateRequestConfig): string {
generateDeleteRequestTemplate(config: ModelUpdateRequestConfig, ctx: TransformerContextProvider): string {
return generateDeleteRequestTemplate(config.modelName, config.isSyncEnabled);
}

generateUpdateInitSlotTemplate(config: ModelCreateInitSlotConfig): string {
return generateUpdateInitSlotTemplate(config.modelConfig);
}

generateGetRequestTemplate(config: ModelRequestConfig): string {
generateGetRequestTemplate(config: ModelRequestConfig, ctx: TransformerContextProvider): string {
return generateGetRequestTemplate();
}

generateGetResponseTemplate(config: ModelUpdateRequestConfig): string {
return generateGetResponseTemplate(config.isSyncEnabled);
}

generateListRequestTemplate(config: ModelRequestConfig): string {
generateListRequestTemplate(config: ModelRequestConfig, ctx: TransformerContextProvider): string {
return generateListRequestTemplate();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,40 @@ import {
ModelUpdateRequestConfig,
ModelVTLGenerator,
} from './vtl-generator';
import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';

// TODO: This class is created only to show the class structure. This needs a revisit to generate correct resolvers for RDS.
export class RDSModelVTLGenerator implements ModelVTLGenerator {
generateUpdateRequestTemplate(config: ModelUpdateRequestConfig): string {
return generateLambdaUpdateRequestTemplate(config.modelName, config.operationName, config.modelIndexFields ?? ['id']);
generateUpdateRequestTemplate(config: ModelUpdateRequestConfig, ctx: TransformerContextProvider): string {
return generateLambdaUpdateRequestTemplate(config.modelName, config.operationName, config.modelIndexFields ?? ['id'], ctx);
}

generateCreateRequestTemplate(config: ModelCreateRequestConfig): string {
return generateLambdaCreateRequestTemplate(config.modelName, config.operationName);
generateCreateRequestTemplate(config: ModelCreateRequestConfig, ctx: TransformerContextProvider): string {
return generateLambdaCreateRequestTemplate(config.modelName, config.operationName, ctx);
}

generateCreateInitSlotTemplate(config: ModelCreateInitSlotConfig, initializeIdField: boolean): string {
return generateCreateInitSlotTemplate(config.modelConfig, initializeIdField);
}

generateDeleteRequestTemplate(config: ModelUpdateRequestConfig): string {
return generateLambdaDeleteRequestTemplate(config.modelName, config.operationName, config.modelIndexFields ?? ['id']);
generateDeleteRequestTemplate(config: ModelUpdateRequestConfig, ctx: TransformerContextProvider): string {
return generateLambdaDeleteRequestTemplate(config.modelName, config.operationName, config.modelIndexFields ?? ['id'], ctx);
}

generateUpdateInitSlotTemplate(config: ModelCreateInitSlotConfig): string {
return generateUpdateInitSlotTemplate(config.modelConfig);
}

generateGetRequestTemplate(config: ModelRequestConfig): string {
return generateLambdaRequestTemplate(config.modelName, config.operation, config.operationName);
generateGetRequestTemplate(config: ModelRequestConfig, ctx: TransformerContextProvider): string {
return generateLambdaRequestTemplate(config.modelName, config.operation, config.operationName, ctx);
}

generateGetResponseTemplate(config: ModelUpdateRequestConfig): string {
return generateGetLambdaResponseTemplate(false);
}

generateListRequestTemplate(config: ModelRequestConfig): string {
return generateLambdaListRequestTemplate(config.modelName, config.operation, config.operationName);
generateListRequestTemplate(config: ModelRequestConfig, ctx: TransformerContextProvider): string {
return generateLambdaListRequestTemplate(config.modelName, config.operation, config.operationName, ctx);
}

generateSyncRequestTemplate(config: ModelRequestConfig): string {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
import { ModelDirectiveConfiguration } from '../../directive';

export type ModelRequestConfig = {
Expand Down Expand Up @@ -31,14 +32,14 @@ export type ModelDefaultResponseConfig = ModelRequestConfig & {
};

export interface ModelVTLGenerator {
generateUpdateRequestTemplate(config: ModelUpdateRequestConfig): string;
generateCreateRequestTemplate(config: ModelCreateRequestConfig): string;
generateUpdateRequestTemplate(config: ModelUpdateRequestConfig, ctx: TransformerContextProvider): string;
generateCreateRequestTemplate(config: ModelCreateRequestConfig, ctx: TransformerContextProvider): string;
generateCreateInitSlotTemplate(config: ModelCreateInitSlotConfig, initializeIdField: boolean): string;
generateDeleteRequestTemplate(config: ModelDeleteRequestConfig): string;
generateDeleteRequestTemplate(config: ModelDeleteRequestConfig, ctx: TransformerContextProvider): string;
generateUpdateInitSlotTemplate(config: ModelUpdateInitSlotConfig): string;
generateGetRequestTemplate(config: ModelRequestConfig): string;
generateGetRequestTemplate(config: ModelRequestConfig, ctx: TransformerContextProvider): string;
generateGetResponseTemplate(config: ModelGetResponseConfig): string;
generateListRequestTemplate(config: ModelRequestConfig): string;
generateListRequestTemplate(config: ModelRequestConfig, ctx: TransformerContextProvider): string;
generateSyncRequestTemplate(config: ModelRequestConfig): string;
generateSubscriptionRequestTemplate(): string;
generateSubscriptionResponseTemplate(): string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import {
str,
toJson,
} from 'graphql-mapping-template';
import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
import { ModelDirectiveConfiguration } from '../../directive';
import { constructNonScalarFieldsStatement } from './resolver';

/**
* Generate mapping template that sets default values for create mutation
Expand Down Expand Up @@ -53,7 +55,7 @@ export const generateCreateInitSlotTemplate = (modelConfig: ModelDirectiveConfig
return printBlock('Initialization default values')(compoundExpression(statements));
};

export const generateLambdaCreateRequestTemplate = (tableName: string, operationName: string): string =>
export const generateLambdaCreateRequestTemplate = (tableName: string, operationName: string, ctx: TransformerContextProvider): string =>
printBlock('Invoke RDS Lambda data source')(
compoundExpression([
set(ref('lambdaInput'), obj({})),
Expand All @@ -63,6 +65,7 @@ export const generateLambdaCreateRequestTemplate = (tableName: string, operation
set(ref('lambdaInput.operationName'), str(operationName)),
set(ref('lambdaInput.args.metadata'), obj({})),
set(ref('lambdaInput.args.metadata.keys'), list([])),
constructNonScalarFieldsStatement(tableName, ctx),
qref(
methodCall(ref('lambdaInput.args.metadata.keys.addAll'), methodCall(ref('util.defaultIfNull'), ref('ctx.stash.keys'), list([]))),
),
Expand Down Expand Up @@ -115,7 +118,7 @@ export const generateUpdateInitSlotTemplate = (modelConfig: ModelDirectiveConfig
/**
* Generate VTL template that calls the lambda for an Update mutation
*/
export const generateLambdaUpdateRequestTemplate = (tableName: string, operationName: string, modelIndexFields: string[]): string =>
export const generateLambdaUpdateRequestTemplate = (tableName: string, operationName: string, modelIndexFields: string[], ctx: TransformerContextProvider): string =>
printBlock('Invoke RDS Lambda data source')(
compoundExpression([
set(ref('lambdaInput'), obj({})),
Expand All @@ -125,6 +128,7 @@ export const generateLambdaUpdateRequestTemplate = (tableName: string, operation
set(ref('lambdaInput.operationName'), str(operationName)),
set(ref('lambdaInput.args.metadata'), obj({})),
set(ref('lambdaInput.args.metadata.keys'), list([])),
constructNonScalarFieldsStatement(tableName, ctx),
qref(
methodCall(ref('lambdaInput.args.metadata.keys.addAll'), methodCall(ref('util.defaultIfNull'), ref('ctx.stash.keys'), list([]))),
),
Expand All @@ -146,7 +150,12 @@ export const generateLambdaUpdateRequestTemplate = (tableName: string, operation
/**
* Generate VTL template that calls the lambda for a Delete mutation
*/
export const generateLambdaDeleteRequestTemplate = (tableName: string, operationName: string, modelIndexFields: string[]): string =>
export const generateLambdaDeleteRequestTemplate = (
tableName: string,
operationName: string,
modelIndexFields: string[],
ctx: TransformerContextProvider,
): string =>
printBlock('Invoke RDS Lambda data source')(
compoundExpression([
set(ref('lambdaInput'), obj({})),
Expand All @@ -156,6 +165,7 @@ export const generateLambdaDeleteRequestTemplate = (tableName: string, operation
set(ref('lambdaInput.operationName'), str(operationName)),
set(ref('lambdaInput.args.metadata'), obj({})),
set(ref('lambdaInput.args.metadata.keys'), list([])),
constructNonScalarFieldsStatement(tableName, ctx),
qref(
methodCall(ref('lambdaInput.args.metadata.keys.addAll'), methodCall(ref('util.defaultIfNull'), ref('ctx.stash.keys'), list([]))),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
import { compoundExpression, list, methodCall, obj, printBlock, qref, ref, set, str } from 'graphql-mapping-template';
import { constructNonScalarFieldsStatement } from './resolver';

export const generateLambdaListRequestTemplate = (tableName: string, operation: string, operationName: string): string => {
export const generateLambdaListRequestTemplate = (
tableName: string,
operation: string,
operationName: string,
ctx: TransformerContextProvider,
): string => {
return printBlock('Invoke RDS Lambda data source')(
compoundExpression([
set(ref('lambdaInput'), obj({})),
Expand All @@ -10,6 +17,7 @@ export const generateLambdaListRequestTemplate = (tableName: string, operation:
set(ref('lambdaInput.operationName'), str(operationName)),
set(ref('lambdaInput.args.metadata'), obj({})),
set(ref('lambdaInput.args.metadata.keys'), list([])),
constructNonScalarFieldsStatement(tableName, ctx),
qref(
methodCall(ref('lambdaInput.args.metadata.keys.addAll'), methodCall(ref('util.defaultIfNull'), ref('ctx.stash.keys'), list([]))),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import {
str,
toJson,
} from 'graphql-mapping-template';
import { ResourceConstants } from 'graphql-transformer-common';
import { ResourceConstants, isArrayOrObject } from 'graphql-transformer-common';
import { RDSConnectionSecrets } from '@aws-amplify/graphql-transformer-core';
import { GraphQLAPIProvider, RDSLayerMapping } from '@aws-amplify/graphql-transformer-interfaces';
import { GraphQLAPIProvider, RDSLayerMapping, TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
import { Effect, IRole, Policy, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { IFunction, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';
import path from 'path';
import { VpcConfig } from '@aws-amplify/graphql-transformer-interfaces/src';
import { EnumTypeDefinitionNode, FieldDefinitionNode, Kind, ObjectTypeDefinitionNode } from 'graphql';

/**
* Define RDS Lambda operations
Expand Down Expand Up @@ -293,7 +294,7 @@ export const createRdsPatchingLambdaRole = (roleName: string, stack: Construct,
* @param operation string
* @param operationName string
*/
export const generateLambdaRequestTemplate = (tableName: string, operation: string, operationName: string): string =>
export const generateLambdaRequestTemplate = (tableName: string, operation: string, operationName: string, ctx: TransformerContextProvider): string =>
printBlock('Invoke RDS Lambda data source')(
compoundExpression([
set(ref('lambdaInput'), obj({})),
Expand All @@ -303,6 +304,7 @@ export const generateLambdaRequestTemplate = (tableName: string, operation: stri
set(ref('lambdaInput.operationName'), str(operationName)),
set(ref('lambdaInput.args.metadata'), obj({})),
set(ref('lambdaInput.args.metadata.keys'), list([])),
constructNonScalarFieldsStatement(tableName, ctx),
qref(
methodCall(ref('lambdaInput.args.metadata.keys.addAll'), methodCall(ref('util.defaultIfNull'), ref('ctx.stash.keys'), list([]))),
),
Expand Down Expand Up @@ -364,3 +366,14 @@ export const generateDefaultLambdaResponseMappingTemplate = (isSyncEnabled: bool

return printBlock('ResponseTemplate')(compoundExpression(statements));
};

export const getNonScalarFields = (object: ObjectTypeDefinitionNode | undefined, ctx: TransformerContextProvider): string[] => {
if (!object) {
return [];
}
const enums = ctx.output.getTypeDefinitionsOfKind(Kind.ENUM_TYPE_DEFINITION) as EnumTypeDefinitionNode[];
return object.fields?.filter((f: FieldDefinitionNode) => isArrayOrObject(f.type, enums)).map((f) => f.name.value) || [];
};

export const constructNonScalarFieldsStatement = (tableName: string, ctx: TransformerContextProvider): Expression =>
set(ref('lambdaInput.args.metadata.nonScalarFields'), list(getNonScalarFields(ctx.output.getObject(tableName), ctx).map(str)));
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ export abstract class ModelResourceGenerator {
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(
vtlGenerator.generateGetRequestTemplate(requestConfig),
vtlGenerator.generateGetRequestTemplate(requestConfig, ctx),
`${typeName}.${fieldName}.req.vtl`,
),
MappingTemplate.s3MappingTemplateFromString(
Expand Down Expand Up @@ -292,7 +292,7 @@ export abstract class ModelResourceGenerator {
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(
vtlGenerator.generateListRequestTemplate(requestConfig),
vtlGenerator.generateListRequestTemplate(requestConfig, ctx),
`${typeName}.${fieldName}.req.vtl`,
),
MappingTemplate.s3MappingTemplateFromString(
Expand Down Expand Up @@ -338,7 +338,7 @@ export abstract class ModelResourceGenerator {
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(
vtlGenerator.generateCreateRequestTemplate(requestConfig),
vtlGenerator.generateCreateRequestTemplate(requestConfig, ctx),
`${typeName}.${fieldName}.req.vtl`,
),
MappingTemplate.s3MappingTemplateFromString(
Expand Down Expand Up @@ -400,7 +400,7 @@ export abstract class ModelResourceGenerator {
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(
vtlGenerator.generateUpdateRequestTemplate(requestConfig),
vtlGenerator.generateUpdateRequestTemplate(requestConfig, ctx),
`${typeName}.${fieldName}.req.vtl`,
),
MappingTemplate.s3MappingTemplateFromString(
Expand Down Expand Up @@ -461,7 +461,7 @@ export abstract class ModelResourceGenerator {
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(
vtlGenerator.generateDeleteRequestTemplate(requestConfig),
vtlGenerator.generateDeleteRequestTemplate(requestConfig, ctx),
`${typeName}.${fieldName}.req.vtl`,
),
MappingTemplate.s3MappingTemplateFromString(
Expand Down
12 changes: 12 additions & 0 deletions packages/graphql-transformer-common/src/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ export function isScalarOrEnum(type: TypeNode, enums: EnumTypeDefinitionNode[])
}
}

export const isArrayOrObject = (type: TypeNode, enums: EnumTypeDefinitionNode[]): boolean => {
if (type.kind === Kind.NON_NULL_TYPE) {
return isArrayOrObject(type.type, enums);
} else if (type.kind === Kind.LIST_TYPE) {
return true;
} else if (enums.some((e) => e.name.value === type.name.value)) {
return false;
} else {
return !DEFAULT_SCALARS[type.name.value];
}
};

export function isEnum(type: TypeNode, document: DocumentNode) {
const baseType = getBaseType(type);
return document.definitions.find((def) => {
Expand Down

0 comments on commit cbfb017

Please sign in to comment.