UNPKG

19.2 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const cloudform_1 = require("cloudform");
4const appSync_1 = require("cloudform-types/types/appSync");
5const graphql_mapping_template_1 = require("graphql-mapping-template");
6const graphql_transformer_common_1 = require("graphql-transformer-common");
7const ResourceConstants_1 = require("./ResourceConstants");
8const RelationalDBMappingTemplate_1 = require("./RelationalDBMappingTemplate");
9const fs = require("fs-extra");
10const s3BaseUrl = 's3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/${ResolverFileName}';
11const resolverFileName = 'ResolverFileName';
12/**
13 * This Class is responsible for Generating the RDS Resolvers based on the
14 * GraphQL Schema + Metadata of the RDS Cluster (i.e. Primary Keys for Tables).
15 *
16 * It will generate the CRUDL+Q (Create, Retrieve, Update, Delete, List + Queries) Resolvers as
17 * Cloudform Resources so that they may be added on to the base template that the
18 * RelationDBTemplateGenerator creates.
19 */
20class RelationalDBResolverGenerator {
21 constructor(context) {
22 this.document = context.schemaDoc;
23 this.typePrimaryKeyMap = context.typePrimaryKeyMap;
24 this.stringFieldMap = context.stringFieldMap;
25 this.intFieldMap = context.intFieldMap;
26 }
27 /**
28 * Creates the CRUDL+Q Resolvers as a Map of Cloudform Resources. The output can then be
29 * merged with an existing Template's map of Resources.
30 */
31 createRelationalResolvers(resolverFilePath) {
32 let resources = {};
33 this.resolverFilePath = resolverFilePath;
34 this.typePrimaryKeyMap.forEach((value, key) => {
35 const resourceName = key.replace(/[^A-Za-z0-9]/g, '');
36 resources = Object.assign({}, resources, { [resourceName + 'CreateResolver']: this.makeCreateRelationalResolver(key) }, { [resourceName + 'GetResolver']: this.makeGetRelationalResolver(key) }, { [resourceName + 'UpdateResolver']: this.makeUpdateRelationalResolver(key) }, { [resourceName + 'DeleteResolver']: this.makeDeleteRelationalResolver(key) }, { [resourceName + 'ListResolver']: this.makeListRelationalResolver(key) });
37 // TODO: Add Guesstimate Query Resolvers
38 });
39 return resources;
40 }
41 /**
42 * Private Helpers to Generate the CFN Spec for the Resolver Resources
43 */
44 /**
45 * Creates and returns the CFN Spec for the 'Create' Resolver Resource provided
46 * a GraphQL Type as the input
47 *
48 * @param type - the graphql type for which the create resolver will be created
49 * @param mutationTypeName - will be 'Mutation'
50 */
51 makeCreateRelationalResolver(type, mutationTypeName = 'Mutation') {
52 const fieldName = graphql_transformer_common_1.graphqlName('create' + graphql_transformer_common_1.toUpper(type));
53 let createSql = `INSERT INTO ${type} $colStr VALUES $valStr`;
54 let selectSql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.create${graphql_transformer_common_1.toUpper(type)}Input.${this.typePrimaryKeyMap.get(type)}`;
55 const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`;
56 const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`;
57 const reqTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
58 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('cols'), graphql_mapping_template_1.list([])),
59 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('vals'), graphql_mapping_template_1.list([])),
60 graphql_mapping_template_1.forEach(graphql_mapping_template_1.ref('entry'), graphql_mapping_template_1.ref(`ctx.args.create${graphql_transformer_common_1.toUpper(type)}Input.keySet()`), [
61 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('discard'), graphql_mapping_template_1.ref(`cols.add($entry)`)),
62 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('discard'), graphql_mapping_template_1.ref(`vals.add("'$ctx.args.create${graphql_transformer_common_1.toUpper(type)}Input[$entry]'")`))
63 ]),
64 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('valStr'), graphql_mapping_template_1.ref('vals.toString().replace("[","(").replace("]",")")')),
65 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('colStr'), graphql_mapping_template_1.ref('cols.toString().replace("[","(").replace("]",")")')),
66 RelationalDBMappingTemplate_1.default.rdsQuery({
67 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(createSql), graphql_mapping_template_1.str(selectSql)])
68 })
69 ]));
70 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.parseJson($utils.rds.toJsonString($ctx.result))[1][0])'));
71 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
72 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
73 let resolver = new appSync_1.default.Resolver({
74 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
75 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
76 TypeName: mutationTypeName,
77 FieldName: fieldName,
78 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
79 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
80 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
81 [resolverFileName]: reqFileName
82 }),
83 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
84 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
85 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
86 [resolverFileName]: resFileName
87 })
88 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
89 return resolver;
90 }
91 /**
92 * Creates and Returns the CFN Spec for the 'Get' Resolver Resource provided
93 * a GraphQL type
94 *
95 * @param type - the graphql type for which the get resolver will be created
96 * @param queryTypeName - will be 'Query'
97 */
98 makeGetRelationalResolver(type, queryTypeName = 'Query') {
99 const fieldName = graphql_transformer_common_1.graphqlName('get' + graphql_transformer_common_1.toUpper(type));
100 let sql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`;
101 const reqFileName = `${queryTypeName}.${fieldName}.req.vtl`;
102 const resFileName = `${queryTypeName}.${fieldName}.res.vtl`;
103 const reqTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
104 RelationalDBMappingTemplate_1.default.rdsQuery({
105 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(sql)])
106 })
107 ]));
108 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])'));
109 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
110 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
111 let resolver = new appSync_1.default.Resolver({
112 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
113 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
114 FieldName: fieldName,
115 TypeName: queryTypeName,
116 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
117 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
118 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
119 [resolverFileName]: reqFileName
120 }),
121 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
122 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
123 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
124 [resolverFileName]: resFileName
125 })
126 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
127 return resolver;
128 }
129 /**
130 * Creates and Returns the CFN Spec for the 'Update' Resolver Resource provided
131 * a GraphQL type
132 *
133 * @param type - the graphql type for which the update resolver will be created
134 * @param mutationTypeName - will be 'Mutation'
135 */
136 makeUpdateRelationalResolver(type, mutationTypeName = 'Mutation') {
137 const fieldName = graphql_transformer_common_1.graphqlName('update' + graphql_transformer_common_1.toUpper(type));
138 const updateSql = `UPDATE ${type} SET $update WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.update${graphql_transformer_common_1.toUpper(type)}Input.${this.typePrimaryKeyMap.get(type)}`;
139 const selectSql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.update${graphql_transformer_common_1.toUpper(type)}Input.${this.typePrimaryKeyMap.get(type)}`;
140 const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`;
141 const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`;
142 const reqTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
143 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('updateList'), graphql_mapping_template_1.obj({})),
144 graphql_mapping_template_1.forEach(graphql_mapping_template_1.ref('entry'), graphql_mapping_template_1.ref(`ctx.args.update${graphql_transformer_common_1.toUpper(type)}Input.keySet()`), [
145 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('discard'), graphql_mapping_template_1.ref(`updateList.put($entry, "'$ctx.args.update${graphql_transformer_common_1.toUpper(type)}Input[$entry]'")`))
146 ]),
147 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('update'), graphql_mapping_template_1.ref(`updateList.toString().replace("{","").replace("}","")`)),
148 RelationalDBMappingTemplate_1.default.rdsQuery({
149 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(updateSql), graphql_mapping_template_1.str(selectSql)])
150 })
151 ]));
152 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.parseJson($utils.rds.toJsonString($ctx.result))[1][0])'));
153 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
154 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
155 let resolver = new appSync_1.default.Resolver({
156 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
157 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
158 TypeName: mutationTypeName,
159 FieldName: fieldName,
160 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
161 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
162 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
163 [resolverFileName]: reqFileName
164 }),
165 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
166 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
167 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
168 [resolverFileName]: resFileName
169 })
170 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
171 return resolver;
172 }
173 /**
174 * Creates and Returns the CFN Spec for the 'Delete' Resolver Resource provided
175 * a GraphQL type
176 *
177 * @param type - the graphql type for which the delete resolver will be created
178 * @param mutationTypeName - will be 'Mutation'
179 */
180 makeDeleteRelationalResolver(type, mutationTypeName = 'Mutation') {
181 const fieldName = graphql_transformer_common_1.graphqlName('delete' + graphql_transformer_common_1.toUpper(type));
182 const selectSql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`;
183 const deleteSql = `DELETE FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`;
184 const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`;
185 const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`;
186 const reqTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
187 RelationalDBMappingTemplate_1.default.rdsQuery({
188 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(selectSql), graphql_mapping_template_1.str(deleteSql)])
189 })
190 ]));
191 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])'));
192 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
193 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
194 let resolver = new appSync_1.default.Resolver({
195 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
196 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
197 TypeName: mutationTypeName,
198 FieldName: fieldName,
199 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
200 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
201 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
202 [resolverFileName]: reqFileName
203 }),
204 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
205 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
206 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
207 [resolverFileName]: resFileName
208 })
209 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
210 return resolver;
211 }
212 /**
213 * Creates and Returns the CFN Spec for the 'List' Resolver Resource provided
214 * a GraphQL type
215 *
216 * @param type - the graphql type for which the list resolver will be created
217 * @param queryTypeName - will be 'Query'
218 */
219 makeListRelationalResolver(type, queryTypeName = 'Query') {
220 const fieldName = graphql_transformer_common_1.graphqlName('list' + graphql_transformer_common_1.plurality(graphql_transformer_common_1.toUpper(type)));
221 const sql = `SELECT * FROM ${type}`;
222 const reqFileName = `${queryTypeName}.${fieldName}.req.vtl`;
223 const resFileName = `${queryTypeName}.${fieldName}.res.vtl`;
224 const reqTemplate = graphql_mapping_template_1.print(RelationalDBMappingTemplate_1.default.rdsQuery({
225 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(sql)])
226 }));
227 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0])'));
228 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
229 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
230 let resolver = new appSync_1.default.Resolver({
231 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
232 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
233 TypeName: queryTypeName,
234 FieldName: fieldName,
235 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
236 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
237 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
238 [resolverFileName]: reqFileName
239 }),
240 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
241 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
242 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
243 [resolverFileName]: resFileName
244 })
245 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
246 return resolver;
247 }
248}
249exports.default = RelationalDBResolverGenerator;
250//# sourceMappingURL=RelationalDBResolverGenerator.js.map
\No newline at end of file