@cdktn/provider-generator 0.24.0-pre.87 → 0.24.0-pre.89
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.
- package/build/__tests__/edge-provider-schema/builder.d.ts +9 -3
- package/build/__tests__/edge-provider-schema/builder.js +6 -3
- package/build/__tests__/edge-provider-schema/index.js +119 -1
- package/build/__tests__/provider.test.js +4 -1
- package/build/get/__tests__/generator/ephemeral-resources.test.d.ts +2 -0
- package/build/get/__tests__/generator/ephemeral-resources.test.js +49 -0
- package/build/get/__tests__/generator/provider-functions.test.d.ts +2 -0
- package/build/get/__tests__/generator/provider-functions.test.js +176 -0
- package/build/get/__tests__/generator/provider.test.js +9 -1
- package/build/get/__tests__/generator/write-only.test.d.ts +2 -0
- package/build/get/__tests__/generator/write-only.test.js +65 -0
- package/build/get/__tests__/target-versions.test.d.ts +2 -0
- package/build/get/__tests__/target-versions.test.js +171 -0
- package/build/get/constructs-maker.d.ts +8 -1
- package/build/get/constructs-maker.js +13 -4
- package/build/get/generator/emitter/attributes-emitter.d.ts +12 -2
- package/build/get/generator/emitter/attributes-emitter.js +47 -5
- package/build/get/generator/emitter/index.d.ts +1 -0
- package/build/get/generator/emitter/index.js +2 -1
- package/build/get/generator/emitter/provider-functions-emitter.d.ts +11 -0
- package/build/get/generator/emitter/provider-functions-emitter.js +94 -0
- package/build/get/generator/emitter/resource-emitter.d.ts +5 -1
- package/build/get/generator/emitter/resource-emitter.js +83 -7
- package/build/get/generator/models/attribute-model.d.ts +2 -0
- package/build/get/generator/models/attribute-model.js +2 -1
- package/build/get/generator/models/index.d.ts +1 -0
- package/build/get/generator/models/index.js +2 -1
- package/build/get/generator/models/provider-function-model.d.ts +102 -0
- package/build/get/generator/models/provider-function-model.js +667 -0
- package/build/get/generator/models/resource-model.d.ts +3 -0
- package/build/get/generator/models/resource-model.js +13 -4
- package/build/get/generator/models/struct.d.ts +2 -0
- package/build/get/generator/models/struct.js +6 -2
- package/build/get/generator/provider-generator.d.ts +2 -0
- package/build/get/generator/provider-generator.js +31 -3
- package/build/get/generator/resource-parser.js +3 -1
- package/package.json +5 -5
- package/src/__tests__/__snapshots__/edge-provider-schema.test.ts.snap +4 -0
- package/src/__tests__/edge-provider-schema/builder.ts +17 -1
- package/src/__tests__/edge-provider-schema/index.ts +124 -1
- package/src/__tests__/provider.test.ts +3 -0
- package/src/get/__tests__/generator/__snapshots__/ephemeral-resources.test.ts.snap +524 -0
- package/src/get/__tests__/generator/__snapshots__/provider-functions.test.ts.snap +444 -0
- package/src/get/__tests__/generator/__snapshots__/write-only.test.ts.snap +262 -0
- package/src/get/__tests__/generator/ephemeral-resources.test.ts +46 -0
- package/src/get/__tests__/generator/fixtures/ephemeral-resources.test.fixture.json +160 -0
- package/src/get/__tests__/generator/fixtures/provider-functions-collision.test.fixture.json +31 -0
- package/src/get/__tests__/generator/fixtures/provider-functions-synthetic.test.fixture.json +192 -0
- package/src/get/__tests__/generator/fixtures/provider-functions.test.fixture.json +170 -0
- package/src/get/__tests__/generator/fixtures/write-only.test.fixture.json +58 -0
- package/src/get/__tests__/generator/provider-functions.test.ts +262 -0
- package/src/get/__tests__/generator/provider.test.ts +8 -0
- package/src/get/__tests__/generator/write-only.test.ts +71 -0
- package/src/get/__tests__/target-versions.test.ts +206 -0
- package/src/get/constructs-maker.ts +43 -3
- package/src/get/generator/emitter/attributes-emitter.ts +61 -6
- package/src/get/generator/emitter/index.ts +1 -0
- package/src/get/generator/emitter/provider-functions-emitter.ts +117 -0
- package/src/get/generator/emitter/resource-emitter.ts +110 -6
- package/src/get/generator/models/attribute-model.ts +3 -0
- package/src/get/generator/models/index.ts +1 -0
- package/src/get/generator/models/provider-function-model.ts +872 -0
- package/src/get/generator/models/resource-model.ts +25 -3
- package/src/get/generator/models/struct.ts +9 -1
- package/src/get/generator/provider-generator.ts +70 -4
- package/src/get/generator/resource-parser.ts +2 -0
|
@@ -43,12 +43,14 @@ export class AttributesEmitter {
|
|
|
43
43
|
|
|
44
44
|
switch (getterType._type) {
|
|
45
45
|
case "plain":
|
|
46
|
+
this.emitWriteOnlyGetterDeprecationNotice(att);
|
|
46
47
|
this.code.openBlock(`public get ${att.name}()`);
|
|
47
48
|
this.code.line(`return ${this.determineGetAttCall(att)};`);
|
|
48
49
|
this.code.closeBlock();
|
|
49
50
|
break;
|
|
50
51
|
|
|
51
52
|
case "args":
|
|
53
|
+
this.emitWriteOnlyGetterDeprecationNotice(att);
|
|
52
54
|
this.code.openBlock(
|
|
53
55
|
`public ${att.name}(${getterType.args})${
|
|
54
56
|
getterType.returnType ? ": " + getterType.returnType : ""
|
|
@@ -59,6 +61,7 @@ export class AttributesEmitter {
|
|
|
59
61
|
break;
|
|
60
62
|
|
|
61
63
|
case "stored_class":
|
|
64
|
+
this.emitWriteOnlyGetterDeprecationNotice(att);
|
|
62
65
|
this.code.openBlock(`public get ${att.name}()`);
|
|
63
66
|
this.code.line(`return this.${att.storageName};`);
|
|
64
67
|
this.code.closeBlock();
|
|
@@ -67,6 +70,10 @@ export class AttributesEmitter {
|
|
|
67
70
|
|
|
68
71
|
const setterType = att.setterType;
|
|
69
72
|
|
|
73
|
+
// Write-only usage is registered at resolve time instead (see
|
|
74
|
+
// #emitToTerraform/#emitToHclTerraform and
|
|
75
|
+
// TerraformResource#markWriteOnlyAttribute), so setters intentionally
|
|
76
|
+
// emit no registration call here: they only store the value.
|
|
70
77
|
switch (setterType._type) {
|
|
71
78
|
case "set":
|
|
72
79
|
this.code.openBlock(
|
|
@@ -123,6 +130,21 @@ export class AttributesEmitter {
|
|
|
123
130
|
}
|
|
124
131
|
}
|
|
125
132
|
|
|
133
|
+
// emits a @deprecated JSDoc block above a write-only attribute's getter:
|
|
134
|
+
// providers never persist/return write-only values, so the state-backed
|
|
135
|
+
// getter always reads back null by protocol contract
|
|
136
|
+
private emitWriteOnlyGetterDeprecationNotice(att: AttributeModel) {
|
|
137
|
+
if (!att.isWriteOnly) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const comment = sanitizedComment(this.code);
|
|
142
|
+
comment.line(
|
|
143
|
+
"@deprecated Write-only: the provider never returns this value; reading it always yields null by protocol contract. The getter remains for compatibility and will be removed in a future prebuilt-provider major.",
|
|
144
|
+
);
|
|
145
|
+
comment.end();
|
|
146
|
+
}
|
|
147
|
+
|
|
126
148
|
// returns an invocation of a stored class, e.g. 'new DeplotmentMetadataOutputReference(this, "metadata")'
|
|
127
149
|
private storedClassInit(att: AttributeModel) {
|
|
128
150
|
return att.type.getStoredClassInitializer(att.terraformName);
|
|
@@ -204,7 +226,20 @@ export class AttributesEmitter {
|
|
|
204
226
|
}
|
|
205
227
|
}
|
|
206
228
|
|
|
207
|
-
|
|
229
|
+
/**
|
|
230
|
+
* @param canRegisterProviderFeatureUsage Whether this attribute's mapped
|
|
231
|
+
* value should be wrapped in `this.markWriteOnlyAttribute(...)` when it is
|
|
232
|
+
* write-only. Only true for attributes emitted on classes that extend
|
|
233
|
+
* TerraformResource (managed resources) - struct-level `xToTerraform`/
|
|
234
|
+
* `xToHclTerraform` functions are standalone functions with no `this` of
|
|
235
|
+
* that type, so they never pass this (see ResourceEmitter's
|
|
236
|
+
* `supportsWriteOnlyRegistration`).
|
|
237
|
+
*/
|
|
238
|
+
public emitToHclTerraform(
|
|
239
|
+
att: AttributeModel,
|
|
240
|
+
isStruct: boolean,
|
|
241
|
+
canRegisterProviderFeatureUsage = false,
|
|
242
|
+
) {
|
|
208
243
|
const type = att.type;
|
|
209
244
|
const context = isStruct ? "struct!" : "this";
|
|
210
245
|
const name = isStruct ? att.name : att.storageName;
|
|
@@ -223,7 +258,15 @@ export class AttributesEmitter {
|
|
|
223
258
|
? `${varReference} === undefined ? ${customDefault} : `
|
|
224
259
|
: "";
|
|
225
260
|
|
|
226
|
-
const
|
|
261
|
+
const mappedValue = `${defaultCheck}${type.toHclTerraformFunction}(${varReference})`;
|
|
262
|
+
// markWriteOnlyAttribute() passes `undefined`/`null` straight through
|
|
263
|
+
// unwrapped, so this keeps the "remove undefined attributes" filter in
|
|
264
|
+
// the caller (which checks `value.value !== undefined`) working exactly
|
|
265
|
+
// as before for unset/cleared write-only attributes.
|
|
266
|
+
const value =
|
|
267
|
+
att.isWriteOnly && canRegisterProviderFeatureUsage
|
|
268
|
+
? `this.markWriteOnlyAttribute(${mappedValue})`
|
|
269
|
+
: mappedValue;
|
|
227
270
|
const isBlock = att.type.isComplex;
|
|
228
271
|
const tt = att.type.typeModelType;
|
|
229
272
|
|
|
@@ -235,7 +278,11 @@ export class AttributesEmitter {
|
|
|
235
278
|
this.code.close("},");
|
|
236
279
|
}
|
|
237
280
|
|
|
238
|
-
public emitToTerraform(
|
|
281
|
+
public emitToTerraform(
|
|
282
|
+
att: AttributeModel,
|
|
283
|
+
isStruct: boolean,
|
|
284
|
+
canRegisterProviderFeatureUsage = false,
|
|
285
|
+
) {
|
|
239
286
|
const type = att.type;
|
|
240
287
|
const context = isStruct ? "struct!" : "this";
|
|
241
288
|
const name = isStruct ? att.name : att.storageName;
|
|
@@ -254,8 +301,16 @@ export class AttributesEmitter {
|
|
|
254
301
|
? `${varReference} === undefined ? ${customDefault} : `
|
|
255
302
|
: "";
|
|
256
303
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
304
|
+
const mappedValue = `${defaultCheck}${type.toTerraformFunction}(${varReference})`;
|
|
305
|
+
// markWriteOnlyAttribute() passes `undefined`/`null` straight through
|
|
306
|
+
// unwrapped, so an unset/cleared write-only attribute still vanishes
|
|
307
|
+
// from the returned object exactly as before (see the "skip undefined"
|
|
308
|
+
// step in tokens/private/resolve.ts).
|
|
309
|
+
const value =
|
|
310
|
+
att.isWriteOnly && canRegisterProviderFeatureUsage
|
|
311
|
+
? `this.markWriteOnlyAttribute(${mappedValue})`
|
|
312
|
+
: mappedValue;
|
|
313
|
+
|
|
314
|
+
this.code.line(`${att.terraformName}: ${value},`);
|
|
260
315
|
}
|
|
261
316
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Copyright (c) HashiCorp, Inc
|
|
2
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
3
|
+
import { CodeMaker } from "codemaker";
|
|
4
|
+
import {
|
|
5
|
+
ProviderFunctionModel,
|
|
6
|
+
ProviderFunctionsModel,
|
|
7
|
+
} from "../models/provider-function-model";
|
|
8
|
+
import { sanitizedComment } from "../sanitized-comments";
|
|
9
|
+
|
|
10
|
+
const PROVIDER_LOCAL_NAME_JSDOC =
|
|
11
|
+
"The local name of the provider in required_providers; defaults to the registry short name. Override when the provider is declared under a different local name — aliases do not change the namespace, local names do.";
|
|
12
|
+
|
|
13
|
+
export class ProviderFunctionsEmitter {
|
|
14
|
+
constructor(private readonly code: CodeMaker) {}
|
|
15
|
+
|
|
16
|
+
public emit(model: ProviderFunctionsModel) {
|
|
17
|
+
this.code.line(`import * as cdktn from 'cdktn';`);
|
|
18
|
+
this.code.line();
|
|
19
|
+
this.emitClass(model);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private emitClass(model: ProviderFunctionsModel) {
|
|
23
|
+
const comment = sanitizedComment(this.code);
|
|
24
|
+
comment.line(
|
|
25
|
+
`Provider-defined functions of the ${model.providerName} provider.`,
|
|
26
|
+
);
|
|
27
|
+
comment.end();
|
|
28
|
+
this.code.openBlock(`export class ${model.className}`);
|
|
29
|
+
|
|
30
|
+
this.emitConstructor();
|
|
31
|
+
|
|
32
|
+
for (const fn of model.functions) {
|
|
33
|
+
this.emitMethod(fn);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.code.closeBlock();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private emitConstructor() {
|
|
40
|
+
// A TypeScript parameter property (`constructor(private readonly x: T)`)
|
|
41
|
+
// requires the compiler to synthesize a `this.x = x;` assignment - that's
|
|
42
|
+
// a real transform, not a type-only erasure, so it isn't recognized by
|
|
43
|
+
// Node's native type-stripping (`node file.ts` with no bundler/transpiler,
|
|
44
|
+
// e.g. `--experimental-strip-types`), which throws
|
|
45
|
+
// `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX` on sight of one. Declare the field
|
|
46
|
+
// and assign it in the constructor body instead, which strips cleanly.
|
|
47
|
+
this.code.line(`private readonly providerLocalName: string;`);
|
|
48
|
+
this.code.line();
|
|
49
|
+
const comment = sanitizedComment(this.code);
|
|
50
|
+
comment.line(`@param providerLocalName ${PROVIDER_LOCAL_NAME_JSDOC}`);
|
|
51
|
+
comment.end();
|
|
52
|
+
this.code.openBlock(`constructor(providerLocalName: string)`);
|
|
53
|
+
this.code.line(`this.providerLocalName = providerLocalName;`);
|
|
54
|
+
this.code.closeBlock();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private emitMethod(fn: ProviderFunctionModel) {
|
|
58
|
+
this.code.line();
|
|
59
|
+
|
|
60
|
+
const comment = sanitizedComment(this.code);
|
|
61
|
+
const description = fn.description ?? fn.summary;
|
|
62
|
+
if (description) comment.line(description);
|
|
63
|
+
if (fn.deprecationMessage) {
|
|
64
|
+
comment.line(`@deprecated ${fn.deprecationMessage}`);
|
|
65
|
+
}
|
|
66
|
+
// The variadic parameter's docstringType is already wrapped in
|
|
67
|
+
// `Array<...>` by buildFunctionModel (see provider-function-model.ts),
|
|
68
|
+
// so it shares this one `@param` emission path with every fixed
|
|
69
|
+
// parameter instead of needing its own wrapping here.
|
|
70
|
+
const allParameters = fn.variadicParameter
|
|
71
|
+
? [...fn.parameters, fn.variadicParameter]
|
|
72
|
+
: fn.parameters;
|
|
73
|
+
for (const param of allParameters) {
|
|
74
|
+
// Official JSDoc grammar: only the type expression goes inside the
|
|
75
|
+
// braces; prose (the schema description plus any generated
|
|
76
|
+
// list/set/nullability guidance - see the model's `docstringNote`)
|
|
77
|
+
// follows the parameter name after ` - `.
|
|
78
|
+
const prose = [param.description, param.docstringNote]
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join(" ");
|
|
81
|
+
comment.line(
|
|
82
|
+
`@param {${param.docstringType}} ${param.name}${prose ? ` - ${prose}` : ""}`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
comment.line(
|
|
86
|
+
`@returns {${fn.returnDocstringType}}${fn.returnDocstringNote ? ` ${fn.returnDocstringNote}` : ""}`,
|
|
87
|
+
);
|
|
88
|
+
comment.end();
|
|
89
|
+
|
|
90
|
+
const methodParams = [
|
|
91
|
+
...fn.parameters.map(
|
|
92
|
+
(p) => `${p.name}${p.optional ? "?" : ""}: ${p.tsType}`,
|
|
93
|
+
),
|
|
94
|
+
...(fn.variadicParameter
|
|
95
|
+
? // `Array<T>` rather than `T[]`: for a union element type (e.g.
|
|
96
|
+
// `boolean | cdktn.IResolvable`), bare `T[]` binds as `boolean |
|
|
97
|
+
// (cdktn.IResolvable[])` instead of `(boolean | cdktn.IResolvable)[]`.
|
|
98
|
+
[
|
|
99
|
+
`${fn.variadicParameter.name}: Array<${fn.variadicParameter.tsType}>`,
|
|
100
|
+
]
|
|
101
|
+
: []),
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const invokeArgs = [
|
|
105
|
+
...fn.parameters.map((p) => p.name),
|
|
106
|
+
...(fn.variadicParameter ? [`...${fn.variadicParameter.name}`] : []),
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
const invokeExpression = `cdktn.TerraformProviderFunction.invoke(this.providerLocalName, "${fn.terraformName}", [${invokeArgs.join(", ")}])`;
|
|
110
|
+
|
|
111
|
+
this.code.openBlock(
|
|
112
|
+
`public ${fn.methodName}(${methodParams.join(", ")}): ${fn.returnTsType}`,
|
|
113
|
+
);
|
|
114
|
+
this.code.line(`return ${fn.wrapReturn(invokeExpression)};`);
|
|
115
|
+
this.code.closeBlock();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -8,12 +8,26 @@ import { sanitizedComment } from "../sanitized-comments";
|
|
|
8
8
|
export class ResourceEmitter {
|
|
9
9
|
attributesEmitter: AttributesEmitter;
|
|
10
10
|
|
|
11
|
-
constructor(
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly code: CodeMaker,
|
|
13
|
+
private readonly importExtension: string,
|
|
14
|
+
) {
|
|
12
15
|
this.attributesEmitter = new AttributesEmitter(this.code);
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
public emit(resource: ResourceModel) {
|
|
16
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
|
+
|
|
17
31
|
const comment = sanitizedComment(this.code);
|
|
18
32
|
comment.line(
|
|
19
33
|
`Represents a {@link ${resource.linkToDocs} ${resource.terraformResourceType}}`,
|
|
@@ -26,8 +40,10 @@ export class ResourceEmitter {
|
|
|
26
40
|
this.emitHeader("STATIC PROPERTIES");
|
|
27
41
|
this.emitStaticProperties(resource);
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
if (!resource.isEphemeralResource) {
|
|
44
|
+
this.emitHeader("STATIC Methods");
|
|
45
|
+
this.emitStaticMethods(resource);
|
|
46
|
+
}
|
|
31
47
|
|
|
32
48
|
this.emitHeader("INITIALIZER");
|
|
33
49
|
this.emitInitializer(resource);
|
|
@@ -35,6 +51,11 @@ export class ResourceEmitter {
|
|
|
35
51
|
this.emitHeader("ATTRIBUTES");
|
|
36
52
|
this.emitResourceAttributes(resource);
|
|
37
53
|
|
|
54
|
+
if (resource.isProvider && resource.providerFunctionsModel) {
|
|
55
|
+
this.emitHeader("PROVIDER-DEFINED FUNCTIONS");
|
|
56
|
+
this.emitFunctionsGetter(resource.providerFunctionsModel);
|
|
57
|
+
}
|
|
58
|
+
|
|
38
59
|
// synthesis
|
|
39
60
|
this.emitHeader("SYNTHESIS");
|
|
40
61
|
this.emitResourceSynthesis(resource);
|
|
@@ -43,6 +64,38 @@ export class ResourceEmitter {
|
|
|
43
64
|
this.code.closeBlock(); // construct
|
|
44
65
|
}
|
|
45
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
|
+
|
|
46
99
|
private emitHeader(title: string) {
|
|
47
100
|
this.code.line();
|
|
48
101
|
this.code.line("// " + "=".repeat(title.length));
|
|
@@ -86,8 +139,13 @@ export class ResourceEmitter {
|
|
|
86
139
|
);
|
|
87
140
|
this.code.open(`const attrs = {`);
|
|
88
141
|
|
|
142
|
+
const registerWriteOnlyUsage = this.supportsWriteOnlyRegistration(resource);
|
|
89
143
|
for (const att of resource.synthesizableAttributes) {
|
|
90
|
-
this.attributesEmitter.emitToHclTerraform(
|
|
144
|
+
this.attributesEmitter.emitToHclTerraform(
|
|
145
|
+
att,
|
|
146
|
+
false,
|
|
147
|
+
registerWriteOnlyUsage,
|
|
148
|
+
);
|
|
91
149
|
}
|
|
92
150
|
|
|
93
151
|
this.code.close(`};`);
|
|
@@ -112,14 +170,38 @@ export class ResourceEmitter {
|
|
|
112
170
|
);
|
|
113
171
|
this.code.open(`return {`);
|
|
114
172
|
|
|
173
|
+
const registerWriteOnlyUsage = this.supportsWriteOnlyRegistration(resource);
|
|
115
174
|
for (const att of resource.synthesizableAttributes) {
|
|
116
|
-
this.attributesEmitter.emitToTerraform(
|
|
175
|
+
this.attributesEmitter.emitToTerraform(
|
|
176
|
+
att,
|
|
177
|
+
false,
|
|
178
|
+
registerWriteOnlyUsage,
|
|
179
|
+
);
|
|
117
180
|
}
|
|
118
181
|
|
|
119
182
|
this.code.close(`};`);
|
|
120
183
|
this.code.closeBlock();
|
|
121
184
|
}
|
|
122
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
|
+
|
|
123
205
|
private emitResourceAttributes(resource: ResourceModel) {
|
|
124
206
|
for (const att of resource.attributes) {
|
|
125
207
|
this.attributesEmitter.emit(
|
|
@@ -136,7 +218,13 @@ export class ResourceEmitter {
|
|
|
136
218
|
comment.line(
|
|
137
219
|
`Create a new {@link ${resource.linkToDocs} ${
|
|
138
220
|
resource.terraformResourceType
|
|
139
|
-
}} ${
|
|
221
|
+
}} ${
|
|
222
|
+
resource.isDataSource
|
|
223
|
+
? "Data Source"
|
|
224
|
+
: resource.isEphemeralResource
|
|
225
|
+
? "Ephemeral Resource"
|
|
226
|
+
: "Resource"
|
|
227
|
+
}`,
|
|
140
228
|
);
|
|
141
229
|
comment.line(``);
|
|
142
230
|
comment.line(`@param scope The scope in which to define this construct`);
|
|
@@ -151,6 +239,8 @@ export class ResourceEmitter {
|
|
|
151
239
|
|
|
152
240
|
if (resource.isProvider) {
|
|
153
241
|
this.emitProviderSuper(resource);
|
|
242
|
+
} else if (resource.isEphemeralResource) {
|
|
243
|
+
this.emitEphemeralResourceSuper(resource);
|
|
154
244
|
} else {
|
|
155
245
|
this.emitResourceSuper(resource);
|
|
156
246
|
}
|
|
@@ -185,6 +275,20 @@ export class ResourceEmitter {
|
|
|
185
275
|
this.code.close(`});`);
|
|
186
276
|
}
|
|
187
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
|
+
|
|
188
292
|
private emitProviderSuper(resource: ResourceModel) {
|
|
189
293
|
this.code.open(`super(scope, id, {`);
|
|
190
294
|
this.code.line(
|
|
@@ -37,6 +37,7 @@ export interface AttributeModelOptions {
|
|
|
37
37
|
provider: boolean;
|
|
38
38
|
required: boolean;
|
|
39
39
|
forcePlainGetterType?: boolean; // used for skipping attribute type attributes that use the SkippedAttributeTypeModel which returns an interpolation and has no stored type
|
|
40
|
+
isWriteOnly?: boolean; // provider protocol write-only attribute: providers never persist/return its value, see RFC-04
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export function escapeAttributeName(name: string) {
|
|
@@ -74,6 +75,7 @@ export class AttributeModel {
|
|
|
74
75
|
public required: boolean;
|
|
75
76
|
public forcePlainGetterType?: boolean;
|
|
76
77
|
private loggedStoredClassUnavailable = false;
|
|
78
|
+
public isWriteOnly: boolean;
|
|
77
79
|
|
|
78
80
|
constructor(options: AttributeModelOptions) {
|
|
79
81
|
this.storageName = options.storageName;
|
|
@@ -87,6 +89,7 @@ export class AttributeModel {
|
|
|
87
89
|
this.provider = options.provider;
|
|
88
90
|
this.required = options.required;
|
|
89
91
|
this.forcePlainGetterType = options.forcePlainGetterType;
|
|
92
|
+
this.isWriteOnly = !!options.isWriteOnly;
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
public get typeDefinition() {
|