@terra-graph/conventions-aws 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +25 -0
  3. package/dist/cjs/conventions/dataflow/profiles/base.js +89 -0
  4. package/dist/cjs/conventions/dataflow/profiles/dot.js +44 -0
  5. package/dist/cjs/conventions/dataflow/profiles/dot.test.js +147 -0
  6. package/dist/cjs/conventions/dataflow/rules.js +4 -0
  7. package/dist/cjs/conventions/dataflow/rules.test.js +11 -0
  8. package/dist/cjs/conventions/dataflow/rulesets.js +751 -0
  9. package/dist/cjs/conventions/dataflow/rulesets.test.js +27 -0
  10. package/dist/cjs/conventions/index.js +7 -0
  11. package/dist/cjs/conventions/index.test.js +8 -0
  12. package/dist/cjs/index.js +70 -0
  13. package/dist/cjs/index.test.js +58 -0
  14. package/dist/cjs/namespaces.js +14 -0
  15. package/dist/cjs/namespaces.test.js +17 -0
  16. package/dist/cjs/package.json +1 -0
  17. package/dist/cjs/plugins/AwsApiGateway.js +218 -0
  18. package/dist/cjs/plugins/AwsApiGateway.test.js +427 -0
  19. package/dist/cjs/plugins/AwsIam.js +432 -0
  20. package/dist/cjs/plugins/AwsIam.test.js +468 -0
  21. package/dist/cjs/plugins/AwsS3.js +107 -0
  22. package/dist/cjs/plugins/AwsS3.test.js +102 -0
  23. package/dist/cjs/plugins/AwsSns.js +52 -0
  24. package/dist/cjs/plugins/AwsSns.test.js +55 -0
  25. package/dist/cjs/plugins/AwsTransferFamily.js +105 -0
  26. package/dist/cjs/plugins/AwsTransferFamily.test.js +279 -0
  27. package/dist/cjs/rules/dot.js +36 -0
  28. package/dist/cjs/rules/dot.test.js +42 -0
  29. package/dist/cjs/rules/general.js +69 -0
  30. package/dist/cjs/rules/general.test.js +47 -0
  31. package/dist/cjs/rulesets/dot.js +25 -0
  32. package/dist/cjs/rulesets/dot.test.js +48 -0
  33. package/dist/esm/conventions/dataflow/profiles/base.d.ts +4 -0
  34. package/dist/esm/conventions/dataflow/profiles/base.js +86 -0
  35. package/dist/esm/conventions/dataflow/profiles/dot.d.ts +4 -0
  36. package/dist/esm/conventions/dataflow/profiles/dot.js +38 -0
  37. package/dist/esm/conventions/dataflow/profiles/dot.test.d.ts +1 -0
  38. package/dist/esm/conventions/dataflow/profiles/dot.test.js +109 -0
  39. package/dist/esm/conventions/dataflow/rules.d.ts +3 -0
  40. package/dist/esm/conventions/dataflow/rules.js +2 -0
  41. package/dist/esm/conventions/dataflow/rules.test.d.ts +1 -0
  42. package/dist/esm/conventions/dataflow/rules.test.js +6 -0
  43. package/dist/esm/conventions/dataflow/rulesets.d.ts +3 -0
  44. package/dist/esm/conventions/dataflow/rulesets.js +749 -0
  45. package/dist/esm/conventions/dataflow/rulesets.test.d.ts +1 -0
  46. package/dist/esm/conventions/dataflow/rulesets.test.js +22 -0
  47. package/dist/esm/conventions/index.d.ts +3 -0
  48. package/dist/esm/conventions/index.js +4 -0
  49. package/dist/esm/conventions/index.test.d.ts +1 -0
  50. package/dist/esm/conventions/index.test.js +6 -0
  51. package/dist/esm/index.d.ts +3 -0
  52. package/dist/esm/index.js +32 -0
  53. package/dist/esm/index.test.d.ts +1 -0
  54. package/dist/esm/index.test.js +53 -0
  55. package/dist/esm/namespaces.d.ts +7 -0
  56. package/dist/esm/namespaces.js +6 -0
  57. package/dist/esm/namespaces.test.d.ts +1 -0
  58. package/dist/esm/namespaces.test.js +15 -0
  59. package/dist/esm/plugins/AwsApiGateway.d.ts +6 -0
  60. package/dist/esm/plugins/AwsApiGateway.js +214 -0
  61. package/dist/esm/plugins/AwsApiGateway.test.d.ts +1 -0
  62. package/dist/esm/plugins/AwsApiGateway.test.js +425 -0
  63. package/dist/esm/plugins/AwsIam.d.ts +32 -0
  64. package/dist/esm/plugins/AwsIam.js +428 -0
  65. package/dist/esm/plugins/AwsIam.test.d.ts +1 -0
  66. package/dist/esm/plugins/AwsIam.test.js +466 -0
  67. package/dist/esm/plugins/AwsS3.d.ts +12 -0
  68. package/dist/esm/plugins/AwsS3.js +103 -0
  69. package/dist/esm/plugins/AwsS3.test.d.ts +1 -0
  70. package/dist/esm/plugins/AwsS3.test.js +100 -0
  71. package/dist/esm/plugins/AwsSns.d.ts +6 -0
  72. package/dist/esm/plugins/AwsSns.js +48 -0
  73. package/dist/esm/plugins/AwsSns.test.d.ts +1 -0
  74. package/dist/esm/plugins/AwsSns.test.js +53 -0
  75. package/dist/esm/plugins/AwsTransferFamily.d.ts +6 -0
  76. package/dist/esm/plugins/AwsTransferFamily.js +101 -0
  77. package/dist/esm/plugins/AwsTransferFamily.test.d.ts +1 -0
  78. package/dist/esm/plugins/AwsTransferFamily.test.js +277 -0
  79. package/dist/esm/rules/dot.d.ts +3 -0
  80. package/dist/esm/rules/dot.js +34 -0
  81. package/dist/esm/rules/dot.test.d.ts +1 -0
  82. package/dist/esm/rules/dot.test.js +37 -0
  83. package/dist/esm/rules/general.d.ts +3 -0
  84. package/dist/esm/rules/general.js +67 -0
  85. package/dist/esm/rules/general.test.d.ts +1 -0
  86. package/dist/esm/rules/general.test.js +42 -0
  87. package/dist/esm/rulesets/dot.d.ts +3 -0
  88. package/dist/esm/rulesets/dot.js +23 -0
  89. package/dist/esm/rulesets/dot.test.d.ts +1 -0
  90. package/dist/esm/rulesets/dot.test.js +43 -0
  91. package/package.json +45 -0
@@ -0,0 +1,427 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@terra-graph/core");
4
+ const AwsApiGateway_js_1 = require("./AwsApiGateway.js");
5
+ const buildPhases = () => {
6
+ const plugin = new AwsApiGateway_js_1.AwsApiGateway();
7
+ const result = plugin.build({
8
+ options: {},
9
+ namedRules: new core_1.NamedRuleRegistry(),
10
+ namedRuleSets: new core_1.NamedRuleSetRegistry(),
11
+ });
12
+ return (result.phases ?? []).map((phase) => ({
13
+ phase: phase.phase,
14
+ rules: phase.rules.map((rule) => rule.serialize()),
15
+ }));
16
+ };
17
+ const buildRules = () => {
18
+ const plugin = new AwsApiGateway_js_1.AwsApiGateway();
19
+ const result = plugin.build({
20
+ options: {},
21
+ namedRules: new core_1.NamedRuleRegistry(),
22
+ namedRuleSets: new core_1.NamedRuleSetRegistry(),
23
+ });
24
+ return (result.phases?.[1]?.rules ?? []).map((rule) => rule);
25
+ };
26
+ const getCompressRule = () => {
27
+ const [compressRule] = buildRules();
28
+ return compressRule;
29
+ };
30
+ const buildAdapter = (graph) => {
31
+ return new core_1.GraphologyAdapter().withTgGraph(graph);
32
+ };
33
+ describe('AwsApiGateway.build', () => {
34
+ it('shoud build expected aws api gateway cleanup phases', () => {
35
+ const phases = buildPhases();
36
+ expect(phases).toHaveLength(4);
37
+ expect(phases.map((phase) => phase.phase)).toStrictEqual(['main', 'main', 'main', 'main']);
38
+ expect(phases[0]?.rules).toHaveLength(1);
39
+ expect(phases[0]?.rules[0]).toStrictEqual({
40
+ id: 'RemoveNode',
41
+ config: {
42
+ node: {
43
+ attr: {
44
+ key: 'terraform.resource',
45
+ in: [
46
+ 'aws_api_gateway_integration_response',
47
+ 'aws_api_gateway_method_response',
48
+ 'aws_api_gateway_method_settings',
49
+ 'aws_api_gateway_account',
50
+ 'aws_api_gateway_model',
51
+ 'aws_wafv2_web_acl_logging_configuration',
52
+ ],
53
+ },
54
+ },
55
+ },
56
+ });
57
+ expect(phases[1]?.rules).toHaveLength(1);
58
+ expect(phases[1]?.rules[0]?.id).toBe('CompressApiGatewayResourcePath');
59
+ expect(phases[2]?.rules).toHaveLength(5);
60
+ expect(phases[2]?.rules.every((rule) => rule.id === 'EdgeReverse')).toBe(true);
61
+ expect(phases[3]?.rules).toHaveLength(1);
62
+ expect(phases[3]?.rules[0]).toStrictEqual({
63
+ id: 'ConvertNodeToEdge',
64
+ config: {
65
+ node: {
66
+ attr: {
67
+ key: 'terraform.resource',
68
+ in: [
69
+ 'aws_api_gateway_deployment',
70
+ 'aws_api_gateway_method',
71
+ 'aws_wafv2_web_acl_association',
72
+ ],
73
+ },
74
+ },
75
+ },
76
+ });
77
+ });
78
+ it('shoud collapse chained gateway resource nodes into a compressed path node', () => {
79
+ const restApiId = (0, core_1.asNodeId)('resource.aws_api_gateway_rest_api.my_api');
80
+ const resourceParentId = (0, core_1.asNodeId)('resource.aws_api_gateway_resource.api');
81
+ const resourceChildId = (0, core_1.asNodeId)('resource.aws_api_gateway_resource.users');
82
+ const rootId = (0, core_1.asNodeId)('resource.other.parent');
83
+ const methodNodeId = (0, core_1.asNodeId)('resource.aws_api_gateway_method.get_user');
84
+ const expectedCompressedId = (0, core_1.tgNodeIdFrom)('resource', 'aws_api_gateway_resource.users/api');
85
+ const adapter = buildAdapter({
86
+ schemaVersion: core_1.TG_SCHEMA_VERSION,
87
+ description: {},
88
+ nodes: {
89
+ [restApiId]: {
90
+ id: restApiId,
91
+ label: 'api',
92
+ terraform: {
93
+ kind: 'resource',
94
+ address: 'aws_api_gateway_rest_api.my_api',
95
+ resource: 'aws_api_gateway_rest_api',
96
+ name: 'my_api',
97
+ },
98
+ },
99
+ [resourceChildId]: {
100
+ id: resourceChildId,
101
+ label: 'users',
102
+ terraform: {
103
+ kind: 'resource',
104
+ address: 'aws_api_gateway_resource.users',
105
+ resource: 'aws_api_gateway_resource',
106
+ name: 'users',
107
+ },
108
+ },
109
+ [resourceParentId]: {
110
+ id: resourceParentId,
111
+ label: 'api',
112
+ terraform: {
113
+ kind: 'resource',
114
+ address: 'aws_api_gateway_resource.api',
115
+ resource: 'aws_api_gateway_resource',
116
+ name: 'api',
117
+ },
118
+ },
119
+ [rootId]: {
120
+ id: rootId,
121
+ label: 'parent',
122
+ terraform: {
123
+ kind: 'resource',
124
+ address: 'aws_api_gateway_resource_parent.parent',
125
+ resource: 'aws_api_gateway_rest_api',
126
+ name: 'parent',
127
+ },
128
+ },
129
+ [methodNodeId]: {
130
+ id: methodNodeId,
131
+ label: 'method',
132
+ terraform: {
133
+ kind: 'resource',
134
+ address: 'aws_api_gateway_method.get_user',
135
+ resource: 'aws_api_gateway_method',
136
+ name: 'get_user',
137
+ },
138
+ },
139
+ },
140
+ edges: [
141
+ {
142
+ id: (0, core_1.asEdgeId)('edge-root-child'),
143
+ from: rootId,
144
+ to: resourceParentId,
145
+ attributes: {},
146
+ },
147
+ {
148
+ id: (0, core_1.asEdgeId)('edge-parent-child'),
149
+ from: resourceParentId,
150
+ to: resourceChildId,
151
+ attributes: {},
152
+ },
153
+ {
154
+ id: (0, core_1.asEdgeId)('edge-child-api'),
155
+ from: resourceChildId,
156
+ to: restApiId,
157
+ attributes: {},
158
+ },
159
+ {
160
+ id: (0, core_1.asEdgeId)('edge-child-method'),
161
+ from: resourceChildId,
162
+ to: methodNodeId,
163
+ attributes: {},
164
+ },
165
+ ],
166
+ });
167
+ const compressRule = getCompressRule();
168
+ const restNode = adapter.getNodeAttributes(restApiId);
169
+ if (!restNode) {
170
+ throw new Error('Missing API gateway rest api node');
171
+ }
172
+ compressRule.match(restApiId, restNode, adapter);
173
+ const updated = compressRule.apply(restApiId, restNode, adapter);
174
+ expect(updated).not.toBe(adapter);
175
+ expect(updated.nodeIds()).toContain(expectedCompressedId);
176
+ expect(updated.getNodeAttributes(expectedCompressedId)).toMatchObject({
177
+ terraform: {
178
+ kind: 'resource',
179
+ address: 'aws_api_gateway_resource.users/api',
180
+ resource: 'aws_api_gateway_resource',
181
+ name: 'users/api',
182
+ },
183
+ });
184
+ expect(updated.nodeIds()).not.toContain(resourceChildId);
185
+ expect(updated.nodeIds()).not.toContain(resourceParentId);
186
+ expect(updated.edgesBetween(rootId, expectedCompressedId)).toHaveLength(1);
187
+ expect(updated.edgesBetween(expectedCompressedId, methodNodeId)).toHaveLength(1);
188
+ expect(updated.edgesBetween(expectedCompressedId, restApiId)).toHaveLength(1);
189
+ expect(updated.nodeIds()).not.toContain(resourceChildId);
190
+ });
191
+ it('shoud keep graph unchanged when aws_api_gateway_rest_api has non-resource parent chain', () => {
192
+ const restApiId = (0, core_1.asNodeId)('resource.aws_api_gateway_rest_api.blank');
193
+ const nonApiParentId = (0, core_1.asNodeId)('resource.something.parent');
194
+ const adapter = buildAdapter({
195
+ schemaVersion: core_1.TG_SCHEMA_VERSION,
196
+ description: {},
197
+ nodes: {
198
+ [nonApiParentId]: {
199
+ id: nonApiParentId,
200
+ label: 'parent',
201
+ terraform: {
202
+ kind: 'resource',
203
+ address: 'random.parent',
204
+ resource: 'aws_lambda_function',
205
+ name: 'parent',
206
+ },
207
+ },
208
+ [restApiId]: {
209
+ id: restApiId,
210
+ label: 'api',
211
+ terraform: {
212
+ kind: 'resource',
213
+ address: 'aws_api_gateway_rest_api.blank',
214
+ resource: 'aws_api_gateway_rest_api',
215
+ name: 'blank',
216
+ },
217
+ },
218
+ },
219
+ edges: [
220
+ {
221
+ id: (0, core_1.asEdgeId)('edge-parent-rest'),
222
+ from: nonApiParentId,
223
+ to: restApiId,
224
+ attributes: {},
225
+ },
226
+ ],
227
+ });
228
+ const compressRule = getCompressRule();
229
+ const restNode = adapter.getNodeAttributes(restApiId);
230
+ if (!restNode) {
231
+ throw new Error('Missing API gateway rest api node');
232
+ }
233
+ compressRule.match(restApiId, restNode, adapter);
234
+ const updated = compressRule.apply(restApiId, restNode, adapter);
235
+ expect(updated).toBe(adapter);
236
+ expect(updated.nodeIds()).toContain(nonApiParentId);
237
+ expect(updated.nodeIds()).toContain(restApiId);
238
+ expect(updated.nodeIds()).not.toContain((0, core_1.tgNodeIdFrom)('resource', 'aws_api_gateway_resource.blank'));
239
+ });
240
+ it('shoud do nothing when CompressApiGatewayResourcePath.apply is not matched', () => {
241
+ const restApiId = (0, core_1.asNodeId)('resource.aws_api_gateway_rest_api.blank');
242
+ const adapter = buildAdapter({
243
+ schemaVersion: core_1.TG_SCHEMA_VERSION,
244
+ description: {},
245
+ nodes: {
246
+ [restApiId]: {
247
+ id: restApiId,
248
+ label: 'api',
249
+ terraform: {
250
+ kind: 'resource',
251
+ address: 'aws_api_gateway_rest_api.blank',
252
+ resource: 'aws_api_gateway_rest_api',
253
+ name: 'blank',
254
+ },
255
+ },
256
+ },
257
+ edges: [],
258
+ });
259
+ const compressRule = getCompressRule();
260
+ const restNode = adapter.getNodeAttributes(restApiId);
261
+ if (!restNode) {
262
+ throw new Error('Missing API gateway rest api node');
263
+ }
264
+ const updated = compressRule.apply(restApiId, restNode, adapter);
265
+ expect(updated).toBe(adapter);
266
+ });
267
+ it('shoud keep graph unchanged when predecessors are not a single parent', () => {
268
+ const restApiId = (0, core_1.asNodeId)('resource.aws_api_gateway_rest_api.blank');
269
+ const adapter = buildAdapter({
270
+ schemaVersion: core_1.TG_SCHEMA_VERSION,
271
+ description: {},
272
+ nodes: {
273
+ [restApiId]: {
274
+ id: restApiId,
275
+ label: 'api',
276
+ terraform: {
277
+ kind: 'resource',
278
+ address: 'aws_api_gateway_rest_api.blank',
279
+ resource: 'aws_api_gateway_rest_api',
280
+ name: 'blank',
281
+ },
282
+ },
283
+ },
284
+ edges: [],
285
+ });
286
+ const compressRule = getCompressRule();
287
+ const restNode = adapter.getNodeAttributes(restApiId);
288
+ if (!restNode) {
289
+ throw new Error('Missing API gateway rest api node');
290
+ }
291
+ compressRule.match(restApiId, restNode, adapter);
292
+ const updated = compressRule.apply(restApiId, restNode, adapter);
293
+ expect(updated).toBe(adapter);
294
+ });
295
+ it('shoud not create a compressed node when the nearest resource parent is missing', () => {
296
+ const parentId = (0, core_1.asNodeId)('resource.aws_api_gateway_resource.api');
297
+ const restApiId = (0, core_1.asNodeId)('resource.aws_api_gateway_rest_api.blank');
298
+ const adapter = buildAdapter({
299
+ schemaVersion: core_1.TG_SCHEMA_VERSION,
300
+ description: {},
301
+ nodes: {
302
+ [parentId]: {
303
+ id: parentId,
304
+ label: 'api',
305
+ terraform: {
306
+ kind: 'resource',
307
+ address: 'aws_api_gateway_resource.api',
308
+ resource: 'aws_api_gateway_resource',
309
+ name: 'api',
310
+ },
311
+ },
312
+ [restApiId]: {
313
+ id: restApiId,
314
+ label: 'blank',
315
+ terraform: {
316
+ kind: 'resource',
317
+ address: 'aws_api_gateway_rest_api.blank',
318
+ resource: 'aws_api_gateway_rest_api',
319
+ name: 'blank',
320
+ },
321
+ },
322
+ },
323
+ edges: [
324
+ {
325
+ id: (0, core_1.asEdgeId)('edge-parent-rest'),
326
+ from: parentId,
327
+ to: restApiId,
328
+ attributes: {},
329
+ },
330
+ ],
331
+ });
332
+ const compressRule = getCompressRule();
333
+ const callCounts = new Map();
334
+ const getNodeAttributes = adapter.getNodeAttributes.bind(adapter);
335
+ const spy = jest.spyOn(adapter, 'getNodeAttributes').mockImplementation((nodeId) => {
336
+ if (nodeId === parentId) {
337
+ const calls = (callCounts.get(nodeId) ?? 0) + 1;
338
+ callCounts.set(nodeId, calls);
339
+ if (calls === 1) {
340
+ return getNodeAttributes(nodeId);
341
+ }
342
+ return undefined;
343
+ }
344
+ return getNodeAttributes(nodeId);
345
+ });
346
+ const restNode = adapter.getNodeAttributes(restApiId);
347
+ if (!restNode) {
348
+ throw new Error('Missing API gateway rest api node');
349
+ }
350
+ compressRule.match(restApiId, restNode, adapter);
351
+ const updated = compressRule.apply(restApiId, restNode, adapter);
352
+ spy.mockRestore();
353
+ expect(updated).toBe(adapter);
354
+ });
355
+ it('shoud use the node id as the resource path when the resource name is missing', () => {
356
+ const parentId = (0, core_1.asNodeId)('resource.aws_api_gateway_resource.api');
357
+ const restApiId = (0, core_1.asNodeId)('resource.aws_api_gateway_rest_api.blank');
358
+ const adapter = buildAdapter({
359
+ schemaVersion: core_1.TG_SCHEMA_VERSION,
360
+ description: {},
361
+ nodes: {
362
+ [parentId]: {
363
+ id: parentId,
364
+ label: 'api',
365
+ terraform: {
366
+ kind: 'resource',
367
+ address: 'aws_api_gateway_resource.api',
368
+ resource: 'aws_api_gateway_resource',
369
+ name: 'api',
370
+ },
371
+ },
372
+ [restApiId]: {
373
+ id: restApiId,
374
+ label: 'blank',
375
+ terraform: {
376
+ kind: 'resource',
377
+ address: 'aws_api_gateway_rest_api.blank',
378
+ resource: 'aws_api_gateway_rest_api',
379
+ name: 'blank',
380
+ },
381
+ },
382
+ },
383
+ edges: [
384
+ {
385
+ id: (0, core_1.asEdgeId)('edge-parent-rest'),
386
+ from: parentId,
387
+ to: restApiId,
388
+ attributes: {},
389
+ },
390
+ ],
391
+ });
392
+ const compressRule = getCompressRule();
393
+ const callCounts = new Map();
394
+ const originalGetNodeAttributes = adapter.getNodeAttributes.bind(adapter);
395
+ const spy = jest.spyOn(adapter, 'getNodeAttributes').mockImplementation((nodeId) => {
396
+ const calls = (callCounts.get(nodeId) ?? 0) + 1;
397
+ callCounts.set(nodeId, calls);
398
+ if (nodeId === parentId && calls === 3) {
399
+ return {
400
+ ...originalGetNodeAttributes(nodeId),
401
+ terraform: {
402
+ kind: 'resource',
403
+ address: 'aws_api_gateway_resource.api',
404
+ resource: 'aws_api_gateway_resource',
405
+ },
406
+ };
407
+ }
408
+ return originalGetNodeAttributes(nodeId);
409
+ });
410
+ const restNode = adapter.getNodeAttributes(restApiId);
411
+ if (!restNode) {
412
+ throw new Error('Missing API gateway rest api node');
413
+ }
414
+ compressRule.match(restApiId, restNode, adapter);
415
+ const updated = compressRule.apply(restApiId, restNode, adapter);
416
+ spy.mockRestore();
417
+ const expectedCompressedId = (0, core_1.tgNodeIdFrom)('resource', `aws_api_gateway_resource.${parentId}`);
418
+ expect(updated.nodeIds()).toContain(expectedCompressedId);
419
+ expect(updated.getNodeAttributes(expectedCompressedId)).toMatchObject({
420
+ terraform: {
421
+ address: `aws_api_gateway_resource.${parentId}`,
422
+ name: 'resource.aws_api_gateway_resource.api',
423
+ },
424
+ });
425
+ expect(updated).not.toBe(adapter);
426
+ });
427
+ });