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