private emitResourceType()

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);
  }