UNPKG

19.1 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 resources = Object.assign({}, resources, { [key + 'CreateResolver']: this.makeCreateRelationalResolver(key) }, { [key + 'GetResolver']: this.makeGetRelationalResolver(key) }, { [key + 'UpdateResolver']: this.makeUpdateRelationalResolver(key) }, { [key + 'DeleteResolver']: this.makeDeleteRelationalResolver(key) }, { [key + 'ListResolver']: this.makeListRelationalResolver(key) });
36 // TODO: Add Guesstimate Query Resolvers
37 });
38 return resources;
39 }
40 /**
41 * Private Helpers to Generate the CFN Spec for the Resolver Resources
42 */
43 /**
44 * Creates and returns the CFN Spec for the 'Create' Resolver Resource provided
45 * a GraphQL Type as the input
46 *
47 * @param type - the graphql type for which the create resolver will be created
48 * @param mutationTypeName - will be 'Mutation'
49 */
50 makeCreateRelationalResolver(type, mutationTypeName = 'Mutation') {
51 const fieldName = graphql_transformer_common_1.graphqlName('create' + graphql_transformer_common_1.toUpper(type));
52 let createSql = `INSERT INTO ${type} $colStr VALUES $valStr`;
53 let selectSql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.create${graphql_transformer_common_1.toUpper(type)}Input.${this.typePrimaryKeyMap.get(type)}`;
54 const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`;
55 const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`;
56 const reqTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
57 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('cols'), graphql_mapping_template_1.list([])),
58 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('vals'), graphql_mapping_template_1.list([])),
59 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()`), [
60 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('discard'), graphql_mapping_template_1.ref(`cols.add($entry)`)),
61 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]'")`))
62 ]),
63 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('valStr'), graphql_mapping_template_1.ref('vals.toString().replace("[","(").replace("]",")")')),
64 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('colStr'), graphql_mapping_template_1.ref('cols.toString().replace("[","(").replace("]",")")')),
65 RelationalDBMappingTemplate_1.default.rdsQuery({
66 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(createSql), graphql_mapping_template_1.str(selectSql)])
67 })
68 ]));
69 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.parseJson($utils.rds.toJsonString($ctx.result))[1][0])'));
70 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
71 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
72 let resolver = new appSync_1.default.Resolver({
73 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
74 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
75 TypeName: mutationTypeName,
76 FieldName: fieldName,
77 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
78 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
79 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
80 [resolverFileName]: reqFileName
81 }),
82 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
83 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
84 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
85 [resolverFileName]: resFileName
86 })
87 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
88 return resolver;
89 }
90 /**
91 * Creates and Returns the CFN Spec for the 'Get' Resolver Resource provided
92 * a GraphQL type
93 *
94 * @param type - the graphql type for which the get resolver will be created
95 * @param queryTypeName - will be 'Query'
96 */
97 makeGetRelationalResolver(type, queryTypeName = 'Query') {
98 const fieldName = graphql_transformer_common_1.graphqlName('get' + graphql_transformer_common_1.toUpper(type));
99 let sql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`;
100 const reqFileName = `${queryTypeName}.${fieldName}.req.vtl`;
101 const resFileName = `${queryTypeName}.${fieldName}.res.vtl`;
102 const reqTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
103 RelationalDBMappingTemplate_1.default.rdsQuery({
104 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(sql)])
105 })
106 ]));
107 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])'));
108 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
109 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
110 let resolver = new appSync_1.default.Resolver({
111 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
112 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
113 FieldName: fieldName,
114 TypeName: queryTypeName,
115 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
116 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
117 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
118 [resolverFileName]: reqFileName
119 }),
120 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
121 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
122 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
123 [resolverFileName]: resFileName
124 })
125 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
126 return resolver;
127 }
128 /**
129 * Creates and Returns the CFN Spec for the 'Update' Resolver Resource provided
130 * a GraphQL type
131 *
132 * @param type - the graphql type for which the update resolver will be created
133 * @param mutationTypeName - will be 'Mutation'
134 */
135 makeUpdateRelationalResolver(type, mutationTypeName = 'Mutation') {
136 const fieldName = graphql_transformer_common_1.graphqlName('update' + graphql_transformer_common_1.toUpper(type));
137 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)}`;
138 const selectSql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.update${graphql_transformer_common_1.toUpper(type)}Input.${this.typePrimaryKeyMap.get(type)}`;
139 const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`;
140 const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`;
141 const reqTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
142 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('updateList'), graphql_mapping_template_1.obj({})),
143 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()`), [
144 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]'")`))
145 ]),
146 graphql_mapping_template_1.set(graphql_mapping_template_1.ref('update'), graphql_mapping_template_1.ref(`updateList.toString().replace("{","").replace("}","")`)),
147 RelationalDBMappingTemplate_1.default.rdsQuery({
148 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(updateSql), graphql_mapping_template_1.str(selectSql)])
149 })
150 ]));
151 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.parseJson($utils.rds.toJsonString($ctx.result))[1][0])'));
152 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
153 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
154 let resolver = new appSync_1.default.Resolver({
155 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
156 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
157 TypeName: mutationTypeName,
158 FieldName: fieldName,
159 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
160 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
161 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
162 [resolverFileName]: reqFileName
163 }),
164 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
165 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
166 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
167 [resolverFileName]: resFileName
168 })
169 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
170 return resolver;
171 }
172 /**
173 * Creates and Returns the CFN Spec for the 'Delete' Resolver Resource provided
174 * a GraphQL type
175 *
176 * @param type - the graphql type for which the delete resolver will be created
177 * @param mutationTypeName - will be 'Mutation'
178 */
179 makeDeleteRelationalResolver(type, mutationTypeName = 'Mutation') {
180 const fieldName = graphql_transformer_common_1.graphqlName('delete' + graphql_transformer_common_1.toUpper(type));
181 const selectSql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`;
182 const deleteSql = `DELETE FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`;
183 const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`;
184 const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`;
185 const reqTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
186 RelationalDBMappingTemplate_1.default.rdsQuery({
187 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(selectSql), graphql_mapping_template_1.str(deleteSql)])
188 })
189 ]));
190 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])'));
191 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
192 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
193 let resolver = new appSync_1.default.Resolver({
194 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
195 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
196 TypeName: mutationTypeName,
197 FieldName: fieldName,
198 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
199 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
200 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
201 [resolverFileName]: reqFileName
202 }),
203 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
204 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
205 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
206 [resolverFileName]: resFileName
207 })
208 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
209 return resolver;
210 }
211 /**
212 * Creates and Returns the CFN Spec for the 'List' Resolver Resource provided
213 * a GraphQL type
214 *
215 * @param type - the graphql type for which the list resolver will be created
216 * @param queryTypeName - will be 'Query'
217 */
218 makeListRelationalResolver(type, queryTypeName = 'Query') {
219 const fieldName = graphql_transformer_common_1.graphqlName('list' + graphql_transformer_common_1.plurality(graphql_transformer_common_1.toUpper(type)));
220 const sql = `SELECT * FROM ${type}`;
221 const reqFileName = `${queryTypeName}.${fieldName}.req.vtl`;
222 const resFileName = `${queryTypeName}.${fieldName}.res.vtl`;
223 const reqTemplate = graphql_mapping_template_1.print(RelationalDBMappingTemplate_1.default.rdsQuery({
224 statements: graphql_mapping_template_1.list([graphql_mapping_template_1.str(sql)])
225 }));
226 const resTemplate = graphql_mapping_template_1.print(graphql_mapping_template_1.ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0])'));
227 fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
228 fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
229 let resolver = new appSync_1.default.Resolver({
230 ApiId: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.AppSyncApiId),
231 DataSourceName: cloudform_1.Fn.GetAtt(ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
232 TypeName: queryTypeName,
233 FieldName: fieldName,
234 RequestMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
235 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
236 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
237 [resolverFileName]: reqFileName
238 }),
239 ResponseMappingTemplateS3Location: cloudform_1.Fn.Sub(s3BaseUrl, {
240 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentBucket),
241 [ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey]: cloudform_1.Fn.Ref(ResourceConstants_1.ResourceConstants.PARAMETERS.S3DeploymentRootKey),
242 [resolverFileName]: resFileName
243 })
244 }).dependsOn([ResourceConstants_1.ResourceConstants.RESOURCES.RelationalDatabaseDataSource]);
245 return resolver;
246 }
247}
248exports.default = RelationalDBResolverGenerator;
249//# sourceMappingURL=RelationalDBResolverGenerator.js.map
\No newline at end of file