in tools/@aws-cdk/cfn2ts/lib/codegen.ts [184:377]
private emitResourceType(resourceName: genspec.CodeName, spec: schema.ResourceType): void {
this.beginNamespace(resourceName);
const cfnName = resourceName.specName!.fqn;
//
// Props Bag for this Resource
//
const propsType = this.emitPropsType(resourceName, spec);
if (propsType) {
this.code.line();
}
const docs = typeDocs(cfnName);
//
// The class declaration representing this Resource
//
this.docLink(spec.Documentation, ...[
`A CloudFormation \`${cfnName}\``,
'',
...docs.description.split('\n'),
'',
`@cloudformationResource ${cfnName}`,
'@stability external',
]);
this.openClass(resourceName, RESOURCE_BASE_CLASS);
//
// Static inspectors.
//
const cfnResourceTypeName = `${JSON.stringify(cfnName)}`;
this.code.line('/**');
this.code.line(' * The CloudFormation resource type name for this resource class.');
this.code.line(' */');
this.code.line(`public static readonly CFN_RESOURCE_TYPE_NAME = ${cfnResourceTypeName};`);
if (spec.RequiredTransform) {
this.code.line('/**');
this.code.line(' * The `Transform` a template must use in order to use this resource');
this.code.line(' */');
this.code.line(`public static readonly REQUIRED_TRANSFORM = ${JSON.stringify(spec.RequiredTransform)};`);
}
//
// The static fromCloudFormation() method,
// used in the @aws-cdk/cloudformation-include module
//
this.code.line();
this.code.line('/**');
this.code.line(' * A factory method that creates a new instance of this class from an object');
this.code.line(' * containing the CloudFormation properties of this resource.');
this.code.line(' * Used in the @aws-cdk/cloudformation-include module.');
this.code.line(' *');
this.code.line(' * @internal');
this.code.line(' */');
// eslint-disable-next-line max-len
this.code.openBlock(`public static _fromCloudFormation(scope: ${CONSTRUCT_CLASS}, id: string, resourceAttributes: any, options: ${CFN_PARSE}.FromCloudFormationOptions): ` +
`${resourceName.className}`);
this.code.line('resourceAttributes = resourceAttributes || {};');
if (propsType) {
// translate the template properties to CDK objects
this.code.line('const resourceProperties = options.parser.parseValue(resourceAttributes.Properties);');
// translate to props, using a (module-private) factory function
this.code.line(`const propsResult = ${genspec.fromCfnFactoryName(propsType).fqn}(resourceProperties);`);
// finally, instantiate the resource class
this.code.line(`const ret = new ${resourceName.className}(scope, id, propsResult.value);`);
// save all keys from extraProperties in the resource using property overrides
this.code.openBlock('for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) ');
this.code.line('ret.addPropertyOverride(propKey, propVal);');
this.code.closeBlock();
} else {
// no props type - we simply instantiate the construct without the third argument
this.code.line(`const ret = new ${resourceName.className}(scope, id);`);
}
// handle all non-property attributes
// (retention policies, conditions, metadata, etc.)
this.code.line('options.parser.handleAttributes(ret, resourceAttributes, id);');
this.code.line('return ret;');
this.code.closeBlock();
//
// Attributes
//
const attributes = new Array<genspec.Attribute>();
if (spec.Attributes) {
for (const attributeName of Object.keys(spec.Attributes).sort()) {
const attributeSpec = spec.Attributes![attributeName];
this.code.line();
this.docLink(undefined,
docs.attributes?.[attributeName] ?? '',
`@cloudformationAttribute ${attributeName}`);
const attr = genspec.attributeDefinition(attributeName, attributeSpec);
this.code.line(`public readonly ${attr.propertyName}: ${attr.attributeType};`);
attributes.push(attr);
}
}
//
// Set class properties to match CloudFormation Properties spec
//
let propMap;
if (propsType) {
propMap = this.emitPropsTypeProperties(resourceName, spec.Properties!, Container.Class);
}
//
// Constructor
//
this.code.line();
this.code.line('/**');
this.code.line(` * Create a new ${quoteCode(resourceName.specName!.fqn)}.`);
this.code.line(' *');
this.code.line(' * @param scope - scope in which this resource is defined');
this.code.line(' * @param id - scoped id of the resource');
this.code.line(' * @param props - resource properties');
this.code.line(' */');
const optionalProps = spec.Properties && !Object.values(spec.Properties).some(p => p.Required || false);
const propsArgument = propsType ? `, props: ${propsType.className}${optionalProps ? ' = {}' : ''}` : '';
this.code.openBlock(`constructor(scope: ${CONSTRUCT_CLASS}, id: string${propsArgument})`);
this.code.line(`super(scope, id, { type: ${resourceName.className}.CFN_RESOURCE_TYPE_NAME${propsType ? ', properties: props' : ''} });`);
// verify all required properties
if (spec.Properties) {
for (const propName of Object.keys(spec.Properties)) {
const prop = spec.Properties[propName];
if (prop.Required) {
this.code.line(`${CORE}.requireProperty(props, '${genspec.cloudFormationToScriptName(propName)}', this);`);
}
}
}
if (spec.RequiredTransform) {
this.code.line('// Automatically add the required transform');
this.code.line(`this.stack.addTransform(${resourceName.className}.REQUIRED_TRANSFORM);`);
}
// initialize all attribute properties
for (const at of attributes) {
if (at.attributeType === 'string') {
this.code.line(`this.${at.propertyName} = ${CORE}.Token.asString(${at.constructorArguments});`);
} else if (at.attributeType === 'string[]') {
this.code.line(`this.${at.propertyName} = ${CORE}.Token.asList(${at.constructorArguments});`);
} else if (at.attributeType === 'number') {
this.code.line(`this.${at.propertyName} = ${CORE}.Token.asNumber(${at.constructorArguments});`);
} else if (at.attributeType === genspec.TOKEN_NAME.fqn) {
this.code.line(`this.${at.propertyName} = ${at.constructorArguments};`);
}
}
// initialize all property class members
if (propsType && propMap) {
this.code.line();
for (const prop of Object.values(propMap)) {
if (schema.isTagPropertyName(upcaseFirst(prop)) && schema.isTaggableResource(spec)) {
this.code.line(`this.tags = new ${TAG_MANAGER}(${tagType(spec)}, ${cfnResourceTypeName}, props.${prop}, { tagPropertyName: '${prop}' });`);
} else {
this.code.line(`this.${prop} = props.${prop};`);
}
}
}
//
// Validator
//
this.emitConstructValidator(resourceName);
// End constructor
this.code.closeBlock();
this.code.line();
this.emitTreeAttributes(resourceName);
// setup render properties
if (propsType && propMap) {
this.code.line();
this.emitCloudFormationProperties(propsType, propMap, schema.isTaggableResource(spec));
}
this.closeClass(resourceName);
this.endNamespace(resourceName);
}