1 | import TemplateContext from "./RelationalDBSchemaTransformer";
|
2 | import { DocumentNode } from 'graphql'
|
3 | import { Fn } from 'cloudform'
|
4 | import AppSync from 'cloudform-types/types/appSync'
|
5 | import { print, obj, set, str, list, forEach, ref, compoundExpression } from 'graphql-mapping-template'
|
6 | import { graphqlName, toUpper, plurality } from 'graphql-transformer-common'
|
7 | import { ResourceConstants } from './ResourceConstants'
|
8 | import RelationalDBMappingTemplate from './RelationalDBMappingTemplate'
|
9 | import * as fs from 'fs-extra'
|
10 |
|
11 | const s3BaseUrl = 's3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/${ResolverFileName}'
|
12 | const resolverFileName = 'ResolverFileName'
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | export default class RelationalDBResolverGenerator {
|
22 | document: DocumentNode
|
23 | typePrimaryKeyMap: Map<string, string>;
|
24 | stringFieldMap: Map<string, string[]>
|
25 | intFieldMap: Map<string, string[]>
|
26 | resolverFilePath: string
|
27 |
|
28 | constructor(context: TemplateContext) {
|
29 | this.document = context.schemaDoc
|
30 | this.typePrimaryKeyMap = context.typePrimaryKeyMap
|
31 | this.stringFieldMap = context.stringFieldMap
|
32 | this.intFieldMap = context.intFieldMap
|
33 | }
|
34 |
|
35 | |
36 |
|
37 |
|
38 |
|
39 | public createRelationalResolvers(resolverFilePath: string) {
|
40 | let resources = {}
|
41 | this.resolverFilePath = resolverFilePath
|
42 | this.typePrimaryKeyMap.forEach((value: string, key: string) => {
|
43 | resources = {
|
44 | ...resources,
|
45 | ...{[key + 'CreateResolver']: this.makeCreateRelationalResolver(key)},
|
46 | ...{[key + 'GetResolver']: this.makeGetRelationalResolver(key)},
|
47 | ...{[key + 'UpdateResolver']: this.makeUpdateRelationalResolver(key)},
|
48 | ...{[key + 'DeleteResolver']: this.makeDeleteRelationalResolver(key)},
|
49 | ...{[key + 'ListResolver']: this.makeListRelationalResolver(key)},
|
50 | }
|
51 |
|
52 | })
|
53 |
|
54 | return resources
|
55 | }
|
56 |
|
57 | |
58 |
|
59 |
|
60 |
|
61 | |
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | private makeCreateRelationalResolver(type: string, mutationTypeName: string = 'Mutation') {
|
69 | const fieldName = graphqlName('create' + toUpper(type))
|
70 | let createSql = `INSERT INTO ${type} $colStr VALUES $valStr`
|
71 | let selectSql =
|
72 | `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.create${toUpper(type)}Input.${this.typePrimaryKeyMap.get(type)}`
|
73 |
|
74 | const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`
|
75 | const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`
|
76 |
|
77 | const reqTemplate = print(
|
78 | compoundExpression([
|
79 | set(ref('cols'), list([])),
|
80 | set(ref('vals'), list([])),
|
81 | forEach(
|
82 | ref('entry'),
|
83 | ref(`ctx.args.create${toUpper(type)}Input.keySet()`),
|
84 | [
|
85 | set(ref('discard'), ref(`cols.add($entry)`)),
|
86 | set(ref('discard'), ref(`vals.add("'$ctx.args.create${toUpper(type)}Input[$entry]'")`))
|
87 | ]
|
88 | ),
|
89 | set(ref('valStr'), ref('vals.toString().replace("[","(").replace("]",")")')),
|
90 | set(ref('colStr'), ref('cols.toString().replace("[","(").replace("]",")")')),
|
91 | RelationalDBMappingTemplate.rdsQuery({
|
92 | statements: list([str(createSql), str(selectSql)])
|
93 | })
|
94 | ])
|
95 | )
|
96 |
|
97 | const resTemplate = print(
|
98 | ref('utils.toJson($utils.parseJson($utils.rds.toJsonString($ctx.result))[1][0])')
|
99 | )
|
100 |
|
101 | fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
|
102 | fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
|
103 |
|
104 | let resolver = new AppSync.Resolver ({
|
105 | ApiId: Fn.Ref(ResourceConstants.PARAMETERS.AppSyncApiId),
|
106 | DataSourceName: Fn.GetAtt(ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
|
107 | TypeName: mutationTypeName,
|
108 | FieldName: fieldName,
|
109 | RequestMappingTemplateS3Location: Fn.Sub(
|
110 | s3BaseUrl,
|
111 | {
|
112 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
113 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
114 | [resolverFileName]: reqFileName
|
115 | }
|
116 | ),
|
117 | ResponseMappingTemplateS3Location: Fn.Sub(
|
118 | s3BaseUrl,
|
119 | {
|
120 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
121 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
122 | [resolverFileName]: resFileName
|
123 | }
|
124 | )
|
125 | }).dependsOn([ResourceConstants.RESOURCES.RelationalDatabaseDataSource])
|
126 | return resolver
|
127 | }
|
128 |
|
129 | |
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | private makeGetRelationalResolver(type: string, queryTypeName: string = 'Query') {
|
137 | const fieldName = graphqlName('get' + toUpper(type))
|
138 | let sql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`
|
139 | const reqFileName = `${queryTypeName}.${fieldName}.req.vtl`
|
140 | const resFileName = `${queryTypeName}.${fieldName}.res.vtl`
|
141 |
|
142 | const reqTemplate = print(
|
143 | compoundExpression([
|
144 | RelationalDBMappingTemplate.rdsQuery({
|
145 | statements: list([str(sql)])
|
146 | })
|
147 | ])
|
148 | )
|
149 |
|
150 | const resTemplate = print(
|
151 | ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])')
|
152 | )
|
153 |
|
154 | fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
|
155 | fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
|
156 |
|
157 | let resolver = new AppSync.Resolver ({
|
158 | ApiId: Fn.Ref(ResourceConstants.PARAMETERS.AppSyncApiId),
|
159 | DataSourceName: Fn.GetAtt(ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
|
160 | FieldName: fieldName,
|
161 | TypeName: queryTypeName,
|
162 | RequestMappingTemplateS3Location: Fn.Sub(
|
163 | s3BaseUrl,
|
164 | {
|
165 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
166 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
167 | [resolverFileName]: reqFileName
|
168 | }
|
169 | ),
|
170 | ResponseMappingTemplateS3Location: Fn.Sub(
|
171 | s3BaseUrl,
|
172 | {
|
173 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
174 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
175 | [resolverFileName]: resFileName
|
176 | }
|
177 | )
|
178 | }).dependsOn([ResourceConstants.RESOURCES.RelationalDatabaseDataSource])
|
179 | return resolver
|
180 | }
|
181 |
|
182 | |
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 | private makeUpdateRelationalResolver(type: string, mutationTypeName: string = 'Mutation') {
|
190 | const fieldName = graphqlName('update' + toUpper(type))
|
191 | const updateSql =
|
192 | `UPDATE ${type} SET $update WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.update${toUpper(type)}Input.${this.typePrimaryKeyMap.get(type)}`
|
193 | const selectSql =
|
194 | `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.update${toUpper(type)}Input.${this.typePrimaryKeyMap.get(type)}`
|
195 | const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`
|
196 | const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`
|
197 |
|
198 | const reqTemplate = print(
|
199 | compoundExpression([
|
200 | set(ref('updateList'), obj({})),
|
201 | forEach(
|
202 | ref('entry'),
|
203 | ref(`ctx.args.update${toUpper(type)}Input.keySet()`),
|
204 | [
|
205 | set(ref('discard'), ref(`updateList.put($entry, "'$ctx.args.update${toUpper(type)}Input[$entry]'")`))
|
206 | ]
|
207 | ),
|
208 | set(ref('update'), ref(`updateList.toString().replace("{","").replace("}","")`)),
|
209 | RelationalDBMappingTemplate.rdsQuery({
|
210 | statements: list([str(updateSql), str(selectSql)])
|
211 | })
|
212 | ])
|
213 | )
|
214 |
|
215 | const resTemplate = print(
|
216 | ref('utils.toJson($utils.parseJson($utils.rds.toJsonString($ctx.result))[1][0])')
|
217 | )
|
218 |
|
219 | fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
|
220 | fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
|
221 |
|
222 | let resolver = new AppSync.Resolver ({
|
223 | ApiId: Fn.Ref(ResourceConstants.PARAMETERS.AppSyncApiId),
|
224 | DataSourceName: Fn.GetAtt(ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
|
225 | TypeName: mutationTypeName,
|
226 | FieldName: fieldName,
|
227 | RequestMappingTemplateS3Location: Fn.Sub(
|
228 | s3BaseUrl,
|
229 | {
|
230 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
231 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
232 | [resolverFileName]: reqFileName
|
233 | }
|
234 | ),
|
235 | ResponseMappingTemplateS3Location: Fn.Sub(
|
236 | s3BaseUrl,
|
237 | {
|
238 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
239 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
240 | [resolverFileName]: resFileName
|
241 | }
|
242 | )
|
243 | }).dependsOn([ResourceConstants.RESOURCES.RelationalDatabaseDataSource])
|
244 | return resolver
|
245 | }
|
246 |
|
247 | |
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 | private makeDeleteRelationalResolver(type: string, mutationTypeName: string = 'Mutation') {
|
255 | const fieldName = graphqlName('delete' + toUpper(type))
|
256 | const selectSql = `SELECT * FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`
|
257 | const deleteSql = `DELETE FROM ${type} WHERE ${this.typePrimaryKeyMap.get(type)}=$ctx.args.${this.typePrimaryKeyMap.get(type)}`
|
258 | const reqFileName = `${mutationTypeName}.${fieldName}.req.vtl`
|
259 | const resFileName = `${mutationTypeName}.${fieldName}.res.vtl`
|
260 | const reqTemplate = print(
|
261 | compoundExpression([
|
262 | RelationalDBMappingTemplate.rdsQuery({
|
263 | statements: list([str(selectSql), str(deleteSql)])
|
264 | })
|
265 | ])
|
266 | )
|
267 | const resTemplate = print(
|
268 | ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])')
|
269 | )
|
270 |
|
271 | fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
|
272 | fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
|
273 |
|
274 | let resolver = new AppSync.Resolver ({
|
275 | ApiId: Fn.Ref(ResourceConstants.PARAMETERS.AppSyncApiId),
|
276 | DataSourceName: Fn.GetAtt(ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
|
277 | TypeName: mutationTypeName,
|
278 | FieldName: fieldName,
|
279 | RequestMappingTemplateS3Location: Fn.Sub(
|
280 | s3BaseUrl,
|
281 | {
|
282 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
283 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
284 | [resolverFileName]: reqFileName
|
285 | }
|
286 | ),
|
287 | ResponseMappingTemplateS3Location: Fn.Sub(
|
288 | s3BaseUrl,
|
289 | {
|
290 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
291 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
292 | [resolverFileName]: resFileName
|
293 | }
|
294 | )
|
295 | }).dependsOn([ResourceConstants.RESOURCES.RelationalDatabaseDataSource])
|
296 |
|
297 | return resolver
|
298 | }
|
299 |
|
300 | |
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 |
|
307 | private makeListRelationalResolver(type: string, queryTypeName: string = 'Query') {
|
308 | const fieldName = graphqlName('list' + plurality(toUpper(type)))
|
309 | const sql = `SELECT * FROM ${type}`
|
310 | const reqFileName = `${queryTypeName}.${fieldName}.req.vtl`
|
311 | const resFileName = `${queryTypeName}.${fieldName}.res.vtl`
|
312 | const reqTemplate = print(
|
313 | RelationalDBMappingTemplate.rdsQuery({
|
314 | statements: list([str(sql)])
|
315 | })
|
316 | )
|
317 | const resTemplate = print(
|
318 | ref('utils.toJson($utils.rds.toJsonObject($ctx.result)[0])')
|
319 | )
|
320 |
|
321 | fs.writeFileSync(`${this.resolverFilePath}/${reqFileName}`, reqTemplate, 'utf8');
|
322 | fs.writeFileSync(`${this.resolverFilePath}/${resFileName}`, resTemplate, 'utf8');
|
323 |
|
324 | let resolver = new AppSync.Resolver ({
|
325 | ApiId: Fn.Ref(ResourceConstants.PARAMETERS.AppSyncApiId),
|
326 | DataSourceName: Fn.GetAtt(ResourceConstants.RESOURCES.RelationalDatabaseDataSource, 'Name'),
|
327 | TypeName: queryTypeName,
|
328 | FieldName: fieldName,
|
329 | RequestMappingTemplateS3Location: Fn.Sub(
|
330 | s3BaseUrl,
|
331 | {
|
332 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
333 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
334 | [resolverFileName]: reqFileName
|
335 | }
|
336 | ),
|
337 | ResponseMappingTemplateS3Location: Fn.Sub(
|
338 | s3BaseUrl,
|
339 | {
|
340 | [ResourceConstants.PARAMETERS.S3DeploymentBucket]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentBucket),
|
341 | [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: Fn.Ref(ResourceConstants.PARAMETERS.S3DeploymentRootKey),
|
342 | [resolverFileName]: resFileName
|
343 | }
|
344 | )
|
345 | }).dependsOn([ResourceConstants.RESOURCES.RelationalDatabaseDataSource])
|
346 |
|
347 | return resolver
|
348 | }
|
349 | } |
\ | No newline at end of file |