import { JSONSchema4 } from "json-schema"; import { CodeMaker } from 'codemaker'; import { TypeGenerator } from "./codegen-types"; import { pascalCase, camelCase, constantCase } from "change-case"; import * as path from "path" /** * Generates a construct for an API object defined by `def`. */ function getType(namespace: string) { switch (namespace) { case 'data_source_schemas': return 'data'; case 'resource_schemas': return 'resource'; case 'provider': return 'provider'; default: { throw new Error(`unexpected Namespace ${namespace}`); } } } export function emitConstructForApiObject(code: CodeMaker, def: JSONSchema4, provider: string, namespace: string, version: string) { const objectName = def.name; if (!objectName) { throw new Error(`no object name`); } const type = getType(namespace) const scope = type === 'data' ? 'data_' : ''; const baseName = `${scope}${objectName.toLocaleLowerCase()}`; const filePath = path.join(provider, namespace.replace('_schemas', ''), baseName) const sourceFile = `${filePath}.ts`; const optionsStructName = `${pascalCase(baseName)}Props`; const typeGenerator = new TypeGenerator(type); emitFile(); return filePath; function emitFile() { code.openFile(sourceFile); code.line(`// generated by terrastack.io`); code.line(); code.line(`import * as tst from '@terrastack/core';`); code.line(`import * as cdk from '@aws-cdk/core';`); code.line(); emitOptionsStruct(); code.line(); emitConstruct(); code.line(); typeGenerator.generate(code); code.closeFile(sourceFile); } function emitOptionsStruct() { const copy: JSONSchema4 = { ...def }; copy.properties = copy.properties || {}; typeGenerator.addType(optionsStructName, copy); } function emitConstruct() { code.line('/**'); code.line(` * ${ def?.description }`); code.line(` */`); code.openBlock(`export class ${pascalCase(baseName)} extends tst.ResourceObject`); emitProperties(); code.line(); emitInitializer(); code.closeBlock(); } function emitInitializer() { code.openBlock(`public constructor(scope: cdk.Construct, ns: string, options: ${optionsStructName})`); emitInitializerSuper(); emitPropsAssignment(); code.closeBlock(); } function emitInitializerSuper() { code.open(`super(scope, ns, { ...options }, {`); if (type === 'provider') { code.line(`name: "${provider}",`); } else { code.line(`name: "${objectName.toLocaleLowerCase()}",`); } code.line(`schemaType: tst.TerraformSchemaType.${constantCase(type)},`); code.line(`providerVersion: '${version}',`); code.line(`providerName: '${provider}',`); code.line(`rawSchema: \`${JSON.stringify(def).replace(new RegExp('`', 'g'), '\\`')}\``); code.close(`});`); } function emitPropsAssignment() { const propType = typeGenerator.types[`${pascalCase(baseName)}Props`] for (const [ propName, propSpec ] of Object.entries(propType || {})) { if (propSpec.referencable) { if ((type !== 'data' && propName === 'id') || (type === 'data') || !propSpec.assignable) { code.line(`this.${camelCase(propName)} = tst.TfReference.for(this, '${camelCase(propName)}', null);`); } else { code.line(`this.${camelCase(propName)} = tst.TfReference.for(this, '${camelCase(propName)}', options.${camelCase(propName)});`); } } else { if (propName === 'tags') { code.line(`this.${camelCase(propName)} = new cdk.TagManager(cdk.TagType.MAP, '${baseName}', options.tags, { tagPropertyName: 'tags' });`); } else { code.line(`this.${camelCase(propName)} = options.${camelCase(propName)};`); } } } } function emitProperties() { const type = typeGenerator.types[`${pascalCase(baseName)}Props`] for (const [ propName, propSpec ] of Object.entries(type || {})) { const propType = (propName === 'tags') ? 'cdk.TagManager' : propSpec.type code.line(`${camelCase(propName)}${propSpec.referencable ? '' : '?'}: ${propType};`); code.line(); } } } export function findApiObjectDefinitions(schema: JSONSchema4) { const result: {[k: string]: {[k: string]: Array}} = {}; // const result = new Array(); const providers = Object.keys(schema.provider_schemas) for (const provider of providers) { const providerDef = schema.provider_schemas[provider] for (const type of Object.keys(providerDef)) { if (type === 'provider') { if (!result[provider]) result[provider] = {}; if (!result[provider][type]) result[provider][type] = []; const def = Object.assign({}, providerDef[type], {name: `${provider}_${type}`}) result[provider][type] = [def] } else { for (const object of Object.keys(providerDef[type])) { const def = Object.assign({}, providerDef[type][object], {name: object}) if (!result[provider]) result[provider] = {}; if (!result[provider][type]) result[provider][type] = []; result[provider][type].push(def) } } } } return result; }