import { CodeModel , ComplexSchema , isObjectSchema , ObjectSchema , Property , SchemaContext } from "@autorest/codemodel" ; import { Channel } from "@autorest/extension-base" ; import { InterfaceDeclarationStructure , PropertySignatureStructure , StructureKind , TypeAliasDeclarationStructure } from "ts-morph" ; import { getSession } from "../autorestSession" ; import { NameType , normalizeName } from "../utils/nameUtils" ; import { isDictionarySchema } from "./schemaHelpers" ; import { getPropertySignature } from "./getPropertySignature" ; import { getLanguageMetadata } from "../utils/languageHelpers" ; /**
 * Generates interfaces for ObjectSchemas
 */ export function buildObjectInterfaces ( model , importedModels , schemaUsage ) { const objectSchemas = ( model . schemas . objects ?? [ ] ) . filter ( o => o . usage ?. some ( u => schemaUsage . includes ( u ) ) ) ; const objectInterfaces = [ ] ; for ( const objectSchema of objectSchemas ) { const baseName = getObjectBaseName ( objectSchema , schemaUsage ) ; const interfaceDeclaration = getObjectInterfaceDeclaration ( baseName , objectSchema , schemaUsage , importedModels ) ; objectInterfaces . push ( interfaceDeclaration ) ; } return objectInterfaces ; } export function buildPolymorphicAliases ( model , schemaUsage ) { const objectAliases = [ ] ; const objectSchemas = ( model . schemas . objects ?? [ ] ) . filter ( o => o . usage ?. some ( u => schemaUsage . includes ( u ) ) ) ; for ( const objectSchema of objectSchemas ) { const baseName = getObjectBaseName ( objectSchema , schemaUsage ) ; const typeAlias = getPolymorphicTypeAlias ( baseName , objectSchema , schemaUsage ) ; if ( typeAlias ) { objectAliases . push ( typeAlias ) ; } } return objectAliases ; } /**
 * Gets a base name for an object schema this is tipically used with suffixes when building interface or type names
 */ function getObjectBaseName ( objectSchema , schemaUsage ) { const nameSuffix = schemaUsage . includes ( SchemaContext . Output ) ? "Output" : "" ; const name = normalizeName ( getLanguageMetadata ( objectSchema . language ) . name , NameType . Interface , true ) ; return `${ name }${ nameSuffix }` ; } /**
 * If the current object is a Polymorphic parent, we need to create
 * a type alias with the union of its children to enable polymorphism
 */ function getPolymorphicTypeAlias ( baseName , objectSchema , schemaUsage ) { if ( ! isPolymorphicParent ( objectSchema ) ) { return undefined ; } const unionTypes = [ ] ; if ( objectSchema . discriminatorValue ) { unionTypes . push ( `${ baseName }Base` ) ; } for ( const child of objectSchema . children ?. all ?? [ ] ) { const nameSuffix = schemaUsage . includes ( SchemaContext . Output ) ? "Output" : "" ; const name = normalizeName ( getLanguageMetadata ( child . language ) . name , NameType . Interface , true ) ; unionTypes . push ( `${ name }${ nameSuffix }` ) ; } return { kind : StructureKind . TypeAlias , name : baseName , type : unionTypes . join ( " | " ) , isExported : true } ; } /**
 * Builds the interface for the current object schema. If it is a polymorphic
 * root node it will suffix it with Base.
 */ function getObjectInterfaceDeclaration ( baseName , objectSchema , schemaUsage , importedModels ) { let interfaceName = `${ baseName }` ; if ( isPolymorphicParent ( objectSchema ) ) { interfaceName = `${ baseName }Base` ; } const properties = objectSchema . properties ?? [ ] ; let propertySignatures = getPropertySignatures ( properties , schemaUsage , importedModels ) ; propertySignatures = addDiscriminatorProperty ( objectSchema , propertySignatures ) ; const extendFrom = getImmediateParentsNames ( objectSchema , schemaUsage ) ; return { kind : StructureKind . Interface , name : interfaceName , isExported : true , properties : propertySignatures , ... ( extendFrom && { extends : extendFrom } ) } ; } function isPolymorphicParent ( objectSchema ) { return objectSchema . children ?. immediate . length && objectSchema . discriminator ; } function addDiscriminatorProperty ( objectSchema , properties ) { const polymorphicProperty = getDiscriminatorProperty ( objectSchema ) ; if ( polymorphicProperty ) { const filteredProperties = properties . filter ( p => p . name !== polymorphicProperty . name ) ; return [ ... filteredProperties , polymorphicProperty ] ; } return properties ; } /**
 * Finds the name of the property used as discriminator and the discriminator value.
 */ function getDiscriminatorProperty ( objectSchema ) { const { message } = getSession ( ) ; const discriminatorValue = objectSchema . discriminatorValue ; if ( ! discriminatorValue && ! objectSchema . discriminator ) { return undefined ; } const discriminators = getDiscriminatorValue ( objectSchema ) ; const discriminatorPropertyName = getDiscriminatorPropertyName ( objectSchema ) ; if ( discriminators ) { if ( discriminatorPropertyName === undefined ) { message ( { Text : `getDiscriminatorProperty: Expected object ${ getLanguageMetadata ( objectSchema . language ) . name } to have a discriminator in its hierarchy but found none` , Channel : Channel . Warning } ) ; return ; } return { kind : StructureKind . PropertySignature , name : `"${ discriminatorPropertyName }"` , type : discriminators } ; } return undefined ; } /**
 * Finds the closest discriminator property
 */ function getDiscriminatorPropertyName ( objectSchema ) { if ( objectSchema . discriminator !== undefined ) { return objectSchema . discriminator . property . serializedName ; } const allParents = objectSchema . parents ?. all ?? [ ] ; for ( const parent of allParents ) { if ( isObjectSchema ( parent ) && parent . discriminator ) { return parent . discriminator . property . serializedName ; } } return undefined ; } /**
 * Calculates the discriminator values that a given object needs
 */ function getDiscriminatorValue ( objectSchema ) { const discriminatorValue = objectSchema . discriminatorValue ; const children = objectSchema . children ?. immediate ?? [ ] ; if ( discriminatorValue && ! children . length ) { return `"${ discriminatorValue }"` ; } if ( children ) { const discriminatorProperty = objectSchema . discriminator ; if ( ! discriminatorProperty ) { return `"${ discriminatorValue }"` ; } const allChildren = objectSchema . children ?. all ?? [ ] ; const selfDiscriminator = discriminatorValue ? [ `"${ discriminatorValue }"` ] : [ ] ; const childValues = getChildDiscriminatorValues ( allChildren ) . map ( v => `"${ v }"` ) ; return [ ... selfDiscriminator , ... childValues ] . join ( " | " ) ; } return undefined ; } /**
 * Looks into the children and grabs all possible discriminatorValues
 */ function getChildDiscriminatorValues ( children ) { const discriminatorValues = new Set < string > ( ) ; for ( const child of children ) { if ( isObjectSchema ( child ) && child . discriminatorValue ) { discriminatorValues . add ( child . discriminatorValue ) ; } } return [ ... discriminatorValues ] ; } /**
 * Gets a list of types a given object may extend from
 */ function getImmediateParentsNames ( objectSchema , shcemaUsage ) { if ( ! objectSchema . parents ?. immediate ) { return [ ] ; } let extendFrom = [ ] ; if ( objectSchema . parents . immediate . find ( isDictionarySchema ) ) { extendFrom . push ( "Record<string, unknown>" ) ; } const parents = objectSchema . parents . immediate . filter ( p => ! isDictionarySchema ( p ) ) . map ( parent => { const nameSuffix = shcemaUsage . includes ( SchemaContext . Output ) ? "Output" : "" ; const name = `${ normalizeName ( getLanguageMetadata ( parent . language ) . name , NameType . Interface , true ) }${ nameSuffix }` ; return isObjectSchema ( parent ) && isPolymorphicParent ( parent ) ? `${ name }Base` : name ; } ) ; return [ ... parents , ... extendFrom ] ; } function getPropertySignatures ( properties , schemaUsage , importedModels ) { return properties . map ( p => getPropertySignature ( p , schemaUsage , importedModels ) ) ; }