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 ) { switch ( namespace ) { case 'data_source_schemas' : return 'data' ; case 'resource_schemas' : return 'resource' ; case 'provider' : return 'provider' ; default : { throw new Error ( `unexpected Namespace ${ namespace }` ) ; } } } function propertyName ( property ) { if ( property === 'self' ) { return camelCase ( `${ property }Property` ) ; } if ( property . split ( '_' ) [ 0 ] === 'get' ) { return camelCase ( property . replace ( 'get' , 'fetch' ) ) ; } return camelCase ( property ) ; } export function emitConstructForApiObject ( code , def , provider , namespace , version ) { 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 = { ... 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.${ propertyName ( propName ) } = tst.TfReference.for(this, '${ propertyName ( propName ) }', null);` ) ; } else { code . line ( `this.${ propertyName ( propName ) } = tst.TfReference.for(this, '${ propertyName ( propName ) }', options.${ propertyName ( propName ) });` ) ; } } else { if ( propName === 'tags' ) { code . line ( `this.${ propertyName ( propName ) } = new cdk.TagManager(cdk.TagType.MAP, '${ baseName }', options.tags, { tagPropertyName: 'tags' });` ) ; } else { code . line ( `this.${ propertyName ( propName ) } = options.${ propertyName ( 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 ( `${ propertyName ( propName ) }${ propSpec . referencable ? '' : '?' }: ${ propType };` ) ; code . line ( ) ; } } } export function findApiObjectDefinitions ( schema ) { const result = { } ; 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 ; }