@cdktn/provider-generator 0.24.0-pre.9 → 0.24.0-pre.91

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 (124) hide show
  1. package/.spec.swcrc +22 -0
  2. package/LICENSE +355 -0
  3. package/README.md +1 -1
  4. package/build/__tests__/edge-provider-schema/builder.d.ts +9 -3
  5. package/build/__tests__/edge-provider-schema/builder.js +6 -3
  6. package/build/__tests__/edge-provider-schema/cli.js +8 -3
  7. package/build/__tests__/edge-provider-schema/index.js +119 -1
  8. package/build/__tests__/provider.test.js +4 -1
  9. package/build/get/__tests__/generator/ephemeral-resources.test.d.ts +2 -0
  10. package/build/get/__tests__/generator/ephemeral-resources.test.js +49 -0
  11. package/build/get/__tests__/generator/module-generator.test.js +12 -12
  12. package/build/get/__tests__/generator/provider-functions.test.d.ts +2 -0
  13. package/build/get/__tests__/generator/provider-functions.test.js +176 -0
  14. package/build/get/__tests__/generator/provider-source-matching.test.d.ts +2 -0
  15. package/build/get/__tests__/generator/provider-source-matching.test.js +45 -0
  16. package/build/get/__tests__/generator/provider.test.js +9 -1
  17. package/build/get/__tests__/generator/supported-stored-classes.test.d.ts +2 -0
  18. package/build/get/__tests__/generator/supported-stored-classes.test.js +137 -0
  19. package/build/get/__tests__/generator/types.test.js +44 -1
  20. package/build/get/__tests__/generator/write-only.test.d.ts +2 -0
  21. package/build/get/__tests__/generator/write-only.test.js +65 -0
  22. package/build/get/__tests__/target-versions.test.d.ts +2 -0
  23. package/build/get/__tests__/target-versions.test.js +171 -0
  24. package/build/get/constructs-maker.d.ts +8 -1
  25. package/build/get/constructs-maker.js +46 -10
  26. package/build/get/generator/emitter/attributes-emitter.d.ts +13 -2
  27. package/build/get/generator/emitter/attributes-emitter.js +71 -5
  28. package/build/get/generator/emitter/index.d.ts +1 -0
  29. package/build/get/generator/emitter/index.js +2 -1
  30. package/build/get/generator/emitter/provider-functions-emitter.d.ts +11 -0
  31. package/build/get/generator/emitter/provider-functions-emitter.js +94 -0
  32. package/build/get/generator/emitter/resource-emitter.d.ts +5 -1
  33. package/build/get/generator/emitter/resource-emitter.js +83 -7
  34. package/build/get/generator/emitter/struct-emitter.js +10 -10
  35. package/build/get/generator/models/attribute-model.d.ts +4 -0
  36. package/build/get/generator/models/attribute-model.js +50 -4
  37. package/build/get/generator/models/attribute-type-model.d.ts +24 -0
  38. package/build/get/generator/models/attribute-type-model.js +91 -5
  39. package/build/get/generator/models/index.d.ts +1 -0
  40. package/build/get/generator/models/index.js +2 -1
  41. package/build/get/generator/models/provider-function-model.d.ts +102 -0
  42. package/build/get/generator/models/provider-function-model.js +667 -0
  43. package/build/get/generator/models/resource-model.d.ts +3 -0
  44. package/build/get/generator/models/resource-model.js +13 -4
  45. package/build/get/generator/models/struct.d.ts +2 -0
  46. package/build/get/generator/models/struct.js +6 -2
  47. package/build/get/generator/models/supported-stored-classes.d.ts +5 -0
  48. package/build/get/generator/models/supported-stored-classes.js +86 -0
  49. package/build/get/generator/module-generator.js +3 -3
  50. package/build/get/generator/provider-generator.d.ts +2 -0
  51. package/build/get/generator/provider-generator.js +41 -6
  52. package/build/get/generator/resource-parser.js +3 -1
  53. package/jest.config.js +16 -9
  54. package/package.json +28 -27
  55. package/package.sh +1 -1
  56. package/src/__tests__/__snapshots__/edge-provider-schema.test.ts.snap +4 -0
  57. package/src/__tests__/__snapshots__/provider.test.ts.snap +2828 -2828
  58. package/src/__tests__/edge-provider-schema/builder.ts +201 -0
  59. package/src/__tests__/edge-provider-schema/cli.ts +51 -0
  60. package/src/__tests__/edge-provider-schema/index.ts +284 -0
  61. package/src/__tests__/edge-provider-schema.test.ts +24 -0
  62. package/src/__tests__/provider.test.ts +183 -0
  63. package/src/get/__tests__/constructs-maker.test.ts +118 -0
  64. package/src/get/__tests__/generator/__snapshots__/complex-computed-types.test.ts.snap +5 -5
  65. package/src/get/__tests__/generator/__snapshots__/ephemeral-resources.test.ts.snap +524 -0
  66. package/src/get/__tests__/generator/__snapshots__/export-sharding.test.ts.snap +3310 -3310
  67. package/src/get/__tests__/generator/__snapshots__/module-generator.test.ts.snap +355 -355
  68. package/src/get/__tests__/generator/__snapshots__/nested-types.test.ts.snap +8 -8
  69. package/src/get/__tests__/generator/__snapshots__/provider-functions.test.ts.snap +444 -0
  70. package/src/get/__tests__/generator/__snapshots__/provider.test.ts.snap +8 -8
  71. package/src/get/__tests__/generator/__snapshots__/resource-types.test.ts.snap +126 -126
  72. package/src/get/__tests__/generator/__snapshots__/skipped-attributes.test.ts.snap +17 -17
  73. package/src/get/__tests__/generator/__snapshots__/types.test.ts.snap +201 -52
  74. package/src/get/__tests__/generator/__snapshots__/write-only.test.ts.snap +262 -0
  75. package/src/get/__tests__/generator/complex-computed-types.test.ts +28 -0
  76. package/src/get/__tests__/generator/deep-nested-attributes.test.ts +56 -0
  77. package/src/get/__tests__/generator/description-escaping.test.ts +84 -0
  78. package/src/get/__tests__/generator/empty-provider-resources.test.ts +26 -0
  79. package/src/get/__tests__/generator/ephemeral-resources.test.ts +46 -0
  80. package/src/get/__tests__/generator/export-sharding.test.ts +169 -0
  81. package/src/get/__tests__/generator/fixtures/deeply-nested-collections.test.fixture.json +129 -0
  82. package/src/get/__tests__/generator/fixtures/ephemeral-resources.test.fixture.json +160 -0
  83. package/src/get/__tests__/generator/fixtures/provider-functions-collision.test.fixture.json +31 -0
  84. package/src/get/__tests__/generator/fixtures/provider-functions-synthetic.test.fixture.json +192 -0
  85. package/src/get/__tests__/generator/fixtures/provider-functions.test.fixture.json +170 -0
  86. package/src/get/__tests__/generator/fixtures/write-only.test.fixture.json +58 -0
  87. package/src/get/__tests__/generator/import-style.test.ts +129 -0
  88. package/src/get/__tests__/generator/module-generator.test.ts +528 -0
  89. package/src/get/__tests__/generator/nested-types.test.ts +54 -0
  90. package/src/get/__tests__/generator/provider-functions.test.ts +262 -0
  91. package/src/get/__tests__/generator/provider-source-matching.test.ts +84 -0
  92. package/src/get/__tests__/generator/provider.test.ts +59 -0
  93. package/src/get/__tests__/generator/resource-types.test.ts +118 -0
  94. package/src/get/__tests__/generator/skipped-attributes.test.ts +72 -0
  95. package/src/get/__tests__/generator/supported-stored-classes.test.ts +147 -0
  96. package/src/get/__tests__/generator/types.test.ts +686 -0
  97. package/src/get/__tests__/generator/versions-file.test.ts +72 -0
  98. package/src/get/__tests__/generator/write-only.test.ts +71 -0
  99. package/src/get/__tests__/target-versions.test.ts +206 -0
  100. package/src/get/__tests__/util.ts +75 -0
  101. package/src/get/constructs-maker.ts +885 -0
  102. package/src/get/generator/custom-defaults.ts +493 -0
  103. package/src/get/generator/emitter/attributes-emitter.ts +316 -0
  104. package/src/get/generator/emitter/index.ts +6 -0
  105. package/src/get/generator/emitter/provider-functions-emitter.ts +117 -0
  106. package/src/get/generator/emitter/resource-emitter.ts +330 -0
  107. package/src/get/generator/emitter/struct-emitter.ts +683 -0
  108. package/src/get/generator/loop-detection.ts +81 -0
  109. package/src/get/generator/models/attribute-model.ts +273 -0
  110. package/src/get/generator/models/attribute-type-model.ts +560 -0
  111. package/src/get/generator/models/index.ts +8 -0
  112. package/src/get/generator/models/provider-function-model.ts +872 -0
  113. package/src/get/generator/models/resource-model.ts +183 -0
  114. package/src/get/generator/models/scope.ts +54 -0
  115. package/src/get/generator/models/struct.ts +124 -0
  116. package/src/get/generator/models/supported-stored-classes.ts +88 -0
  117. package/src/get/generator/module-generator.ts +234 -0
  118. package/src/get/generator/provider-generator.ts +428 -0
  119. package/src/get/generator/resource-parser.ts +764 -0
  120. package/src/get/generator/sanitized-comments.ts +49 -0
  121. package/src/get/generator/skipped-attributes.ts +27 -0
  122. package/src/index.ts +40 -0
  123. package/src/util.ts +26 -0
  124. package/tsconfig.json +1 -2
@@ -0,0 +1,330 @@
1
+ // Copyright (c) HashiCorp, Inc
2
+ // SPDX-License-Identifier: MPL-2.0
3
+ import { CodeMaker } from "codemaker";
4
+ import { ResourceModel } from "../models";
5
+ import { AttributesEmitter } from "./attributes-emitter";
6
+ import { sanitizedComment } from "../sanitized-comments";
7
+
8
+ export class ResourceEmitter {
9
+ attributesEmitter: AttributesEmitter;
10
+
11
+ constructor(
12
+ private readonly code: CodeMaker,
13
+ private readonly importExtension: string,
14
+ ) {
15
+ this.attributesEmitter = new AttributesEmitter(this.code);
16
+ }
17
+
18
+ public emit(resource: ResourceModel) {
19
+ this.code.line();
20
+
21
+ if (resource.isProvider && resource.providerFunctionsModel) {
22
+ // Sibling-directory import: providers/<provider>/provider/index.ts
23
+ // (the emitted file) and providers/<provider>/provider-functions/
24
+ // index.ts are siblings under providers/<provider>/, so unlike the
25
+ // child-folder struct imports this one has to step up a level.
26
+ this.code.line(
27
+ `import { ${resource.providerFunctionsModel.className} } from '../provider-functions/index${this.importExtension}';`,
28
+ );
29
+ }
30
+
31
+ const comment = sanitizedComment(this.code);
32
+ comment.line(
33
+ `Represents a {@link ${resource.linkToDocs} ${resource.terraformResourceType}}`,
34
+ );
35
+ comment.end();
36
+ this.code.openBlock(
37
+ `export class ${resource.className} extends cdktn.${resource.parentClassName}`,
38
+ );
39
+
40
+ this.emitHeader("STATIC PROPERTIES");
41
+ this.emitStaticProperties(resource);
42
+
43
+ if (!resource.isEphemeralResource) {
44
+ this.emitHeader("STATIC Methods");
45
+ this.emitStaticMethods(resource);
46
+ }
47
+
48
+ this.emitHeader("INITIALIZER");
49
+ this.emitInitializer(resource);
50
+
51
+ this.emitHeader("ATTRIBUTES");
52
+ this.emitResourceAttributes(resource);
53
+
54
+ if (resource.isProvider && resource.providerFunctionsModel) {
55
+ this.emitHeader("PROVIDER-DEFINED FUNCTIONS");
56
+ this.emitFunctionsGetter(resource.providerFunctionsModel);
57
+ }
58
+
59
+ // synthesis
60
+ this.emitHeader("SYNTHESIS");
61
+ this.emitResourceSynthesis(resource);
62
+ this.emitHclResourceSynthesis(resource);
63
+
64
+ this.code.closeBlock(); // construct
65
+ }
66
+
67
+ // Emits a memoized `functions` getter on a provider class that declares
68
+ // provider-defined functions. The local name to invoke functions under is
69
+ // derived from `this.terraformResourceType`, not baked in as the schema
70
+ // name constant: terraform-provider.ts keys both `required_providers` and
71
+ // the `provider` block directly off `terraformResourceType`, so whatever
72
+ // this instance's `terraformResourceType` is IS its correct
73
+ // required_providers local name (it already accounts for a `name`
74
+ // override in the cdktf.json provider constraint, see
75
+ // ConstructsMakerProviderTarget/TerraformProviderConstraint) - the
76
+ // instance always knows its own correct local name, so there is no need
77
+ // for a providerLocalName override parameter here.
78
+ private emitFunctionsGetter(
79
+ model: NonNullable<ResourceModel["providerFunctionsModel"]>,
80
+ ) {
81
+ this.code.line(`private _functions?: ${model.className};`);
82
+ this.code.line();
83
+
84
+ const comment = sanitizedComment(this.code);
85
+ comment.line(
86
+ `Provider-defined functions of the ${model.providerName} provider.`,
87
+ );
88
+ comment.end();
89
+ this.code.openBlock(`public get functions(): ${model.className}`);
90
+ this.code.openBlock(`if (!this._functions)`);
91
+ this.code.line(
92
+ `this._functions = new ${model.className}(this.terraformResourceType);`,
93
+ );
94
+ this.code.closeBlock();
95
+ this.code.line(`return this._functions;`);
96
+ this.code.closeBlock();
97
+ }
98
+
99
+ private emitHeader(title: string) {
100
+ this.code.line();
101
+ this.code.line("// " + "=".repeat(title.length));
102
+ this.code.line(`// ${title}`);
103
+ this.code.line("// " + "=".repeat(title.length));
104
+ }
105
+
106
+ private emitStaticProperties(resource: ResourceModel) {
107
+ this.code.line(
108
+ `public static readonly tfResourceType = "${resource.terraformResourceType}";`,
109
+ );
110
+ }
111
+
112
+ private emitStaticMethods(resource: ResourceModel) {
113
+ const comment = sanitizedComment(this.code);
114
+ comment.line(
115
+ `Generates CDKTN code for importing a ${resource.className} resource upon running "cdktn plan <stack-name>"`,
116
+ );
117
+ comment.line(`@param scope The scope in which to define this construct`);
118
+ comment.line(
119
+ `@param importToId The construct id used in the generated config for the ${resource.className} to import`,
120
+ );
121
+ comment.line(
122
+ `@param importFromId The id of the existing ${resource.className} that should be imported. Refer to the {@link ${resource.linkToDocs}#import import section} in the documentation of this resource for the id to use`,
123
+ );
124
+ comment.line(
125
+ `@param provider? Optional instance of the provider where the ${resource.className} to import is found`,
126
+ );
127
+ comment.end();
128
+ this.code.line(
129
+ `public static generateConfigForImport(scope: Construct, importToId: string, importFromId: string, provider?: cdktn.TerraformProvider) {
130
+ return new cdktn.ImportableResource(scope, importToId, { terraformResourceType: "${resource.terraformResourceType}", importId: importFromId, provider });
131
+ }`,
132
+ );
133
+ }
134
+
135
+ private emitHclResourceSynthesis(resource: ResourceModel) {
136
+ this.code.line();
137
+ this.code.openBlock(
138
+ `protected synthesizeHclAttributes(): { [name: string]: any }`,
139
+ );
140
+ this.code.open(`const attrs = {`);
141
+
142
+ const registerWriteOnlyUsage = this.supportsWriteOnlyRegistration(resource);
143
+ for (const att of resource.synthesizableAttributes) {
144
+ this.attributesEmitter.emitToHclTerraform(
145
+ att,
146
+ false,
147
+ registerWriteOnlyUsage,
148
+ );
149
+ }
150
+
151
+ this.code.close(`};`);
152
+
153
+ if (resource.synthesizableAttributes.length > 0) {
154
+ this.code.line();
155
+ this.code.line(`// remove undefined attributes`);
156
+ this.code.line(
157
+ `return Object.fromEntries(Object.entries(attrs).filter(([_, value]) => value !== undefined && value.value !== undefined ))`,
158
+ );
159
+ } else {
160
+ this.code.line(`return attrs;`);
161
+ }
162
+
163
+ this.code.closeBlock();
164
+ }
165
+
166
+ private emitResourceSynthesis(resource: ResourceModel) {
167
+ this.code.line();
168
+ this.code.openBlock(
169
+ `protected synthesizeAttributes(): { [name: string]: any }`,
170
+ );
171
+ this.code.open(`return {`);
172
+
173
+ const registerWriteOnlyUsage = this.supportsWriteOnlyRegistration(resource);
174
+ for (const att of resource.synthesizableAttributes) {
175
+ this.attributesEmitter.emitToTerraform(
176
+ att,
177
+ false,
178
+ registerWriteOnlyUsage,
179
+ );
180
+ }
181
+
182
+ this.code.close(`};`);
183
+ this.code.closeBlock();
184
+ }
185
+
186
+ // `markWriteOnlyAttribute` (called from generated `synthesizeAttributes()`/
187
+ // `synthesizeHclAttributes()` for write-only attributes, see
188
+ // AttributesEmitter#emitToTerraform/#emitToHclTerraform) lives on
189
+ // TerraformResource - so only managed resources can register write-only
190
+ // usage; data sources, providers, and ephemeral resources only get the
191
+ // deprecated getter.
192
+ //
193
+ // write-only is fundamentally a state concept - the value is passed to
194
+ // the provider but never persisted to state - so gating this on
195
+ // TerraformResource is a semantic choice, not a capability one: ephemeral
196
+ // resources have no state at all, and, consistent with that, no provider
197
+ // schema in the RFC-04 sweep (including vault's 16 ephemeral resources)
198
+ // marks an ephemeral attribute write_only. So ephemeral classes
199
+ // deliberately land in the same "getter only" bucket as data sources and
200
+ // providers.
201
+ private supportsWriteOnlyRegistration(resource: ResourceModel): boolean {
202
+ return resource.parentClassName === "TerraformResource";
203
+ }
204
+
205
+ private emitResourceAttributes(resource: ResourceModel) {
206
+ for (const att of resource.attributes) {
207
+ this.attributesEmitter.emit(
208
+ att,
209
+ this.attributesEmitter.needsResetEscape(att, resource.attributes),
210
+ this.attributesEmitter.needsInputEscape(att, resource.attributes),
211
+ );
212
+ }
213
+ }
214
+
215
+ private emitInitializer(resource: ResourceModel) {
216
+ this.code.line();
217
+ const comment = sanitizedComment(this.code);
218
+ comment.line(
219
+ `Create a new {@link ${resource.linkToDocs} ${
220
+ resource.terraformResourceType
221
+ }} ${
222
+ resource.isDataSource
223
+ ? "Data Source"
224
+ : resource.isEphemeralResource
225
+ ? "Ephemeral Resource"
226
+ : "Resource"
227
+ }`,
228
+ );
229
+ comment.line(``);
230
+ comment.line(`@param scope The scope in which to define this construct`);
231
+ comment.line(
232
+ `@param id The scoped construct ID. Must be unique amongst siblings in the same scope`,
233
+ );
234
+ comment.line(`@param options ${resource.configStruct.attributeType}`);
235
+ comment.end();
236
+ this.code.openBlock(
237
+ `public constructor(scope: Construct, id: string, config: ${resource.configStruct.attributeType})`,
238
+ );
239
+
240
+ if (resource.isProvider) {
241
+ this.emitProviderSuper(resource);
242
+ } else if (resource.isEphemeralResource) {
243
+ this.emitEphemeralResourceSuper(resource);
244
+ } else {
245
+ this.emitResourceSuper(resource);
246
+ }
247
+
248
+ // initialize config properties
249
+ for (const att of resource.configStruct.assignableAttributes) {
250
+ if (att.setterType._type === "stored_class") {
251
+ this.code.line(
252
+ `this.${att.storageName}.internalValue = config.${att.name};`,
253
+ );
254
+ } else {
255
+ this.code.line(`this.${att.storageName} = config.${att.name};`);
256
+ }
257
+ }
258
+
259
+ this.code.closeBlock();
260
+ }
261
+
262
+ private emitResourceSuper(resource: ResourceModel) {
263
+ this.code.open(`super(scope, id, {`);
264
+ this.code.line(
265
+ `terraformResourceType: '${resource.terraformResourceType}',`,
266
+ );
267
+ this.emitTerraformGeneratorMetadata(resource);
268
+ this.code.line(`provider: config.provider,`);
269
+ this.code.line(`dependsOn: config.dependsOn,`);
270
+ this.code.line(`count: config.count,`);
271
+ this.code.line(`lifecycle: config.lifecycle,`);
272
+ this.code.line(`provisioners: config.provisioners,`);
273
+ this.code.line(`connection: config.connection,`);
274
+ this.code.line(`forEach: config.forEach`);
275
+ this.code.close(`});`);
276
+ }
277
+
278
+ private emitEphemeralResourceSuper(resource: ResourceModel) {
279
+ this.code.open(`super(scope, id, {`);
280
+ this.code.line(
281
+ `terraformResourceType: '${resource.terraformResourceType}',`,
282
+ );
283
+ this.emitTerraformGeneratorMetadata(resource);
284
+ this.code.line(`provider: config.provider,`);
285
+ this.code.line(`dependsOn: config.dependsOn,`);
286
+ this.code.line(`count: config.count,`);
287
+ this.code.line(`lifecycle: config.lifecycle,`);
288
+ this.code.line(`forEach: config.forEach`);
289
+ this.code.close(`});`);
290
+ }
291
+
292
+ private emitProviderSuper(resource: ResourceModel) {
293
+ this.code.open(`super(scope, id, {`);
294
+ this.code.line(
295
+ `terraformResourceType: '${resource.terraformResourceType}',`,
296
+ );
297
+ this.emitTerraformGeneratorMetadata(resource);
298
+ this.code.line(
299
+ `terraformProviderSource: '${resource.terraformProviderSource}'`,
300
+ );
301
+ this.code.close(`});`);
302
+ }
303
+
304
+ private emitTerraformGeneratorMetadata(resource: ResourceModel) {
305
+ this.code.open(`terraformGeneratorMetadata: {`);
306
+ this.code.line(
307
+ `providerName: '${resource.provider}'${
308
+ resource.providerVersion || resource.providerVersionConstraint
309
+ ? ","
310
+ : ""
311
+ }`,
312
+ );
313
+
314
+ if (resource.providerVersion) {
315
+ this.code.line(
316
+ `providerVersion: '${resource.providerVersion}'${
317
+ resource.providerVersionConstraint ? "," : ""
318
+ }`,
319
+ );
320
+ }
321
+
322
+ if (resource.providerVersionConstraint) {
323
+ this.code.line(
324
+ `providerVersionConstraint: '${resource.providerVersionConstraint}'`,
325
+ );
326
+ }
327
+
328
+ this.code.close(`},`);
329
+ }
330
+ }