import { CodeModel , ObjectSchema , ComplexSchema , Property , GroupProperty } from "@autorest/codemodel" ; import { ObjectDetails , ObjectKind , PolymorphicObjectDetails , ComposedObjectDetails } from "../models/modelDetails" ; import { getLanguageMetadata } from "../utils/languageHelpers" ; import { normalizeName , NameType } from "../utils/nameUtils" ; import { PropertyDetails } from "../models/modelDetails" ; import { getTypeForSchema , getSchemaParents , getAdditionalProperties , getSchemaTypeDocumentation } from "../utils/schemaHelpers" ; import { extractHeaders } from "../utils/extractHeaders" ; import { getAutorestOptions } from "../autorestSession" ; export function transformObjects ( codeModel , uberParents ) { const clientName = getLanguageMetadata ( codeModel . language ) . name ; const objectSchemas = codeModel . schemas . objects || [ ] ; const headersSchemas = extractHeaders ( codeModel . operationGroups , clientName ) ; const objectDetails = [ ... objectSchemas , ... headersSchemas ] . map ( object => transformObject ( object , uberParents ) ) ; return getObjectDetailsWithHierarchy ( objectDetails ) ; } export function transformObject ( schema , uberParents ) { const metadata = getLanguageMetadata ( schema . language ) ; let name = normalizeName ( metadata . name , NameType . Class , true ) ; const kind = getObjectKind ( schema ) ; let objectDetails = { children : [ ] , parents : [ ] , hasAdditionalProperties : false , kind , name , serializedName : metadata . serializedName , description : metadata . description || undefined , schema , properties : schema . properties ? schema . properties . map ( prop => transformProperty ( prop ) ) : [ ] } ; return getAdditionalObjectDetails ( objectDetails , uberParents ) ; } export function transformProperty ( { language , schema , serializedName , required , readOnly , nullable } ) { const { useCoreV2 } = getAutorestOptions ( ) ; const metadata = getLanguageMetadata ( language ) ; const typeDetails = getTypeForSchema ( schema , false , useCoreV2 ) ; const { typeName , isConstant , defaultValue } = typeDetails ; const schemaDescription = getSchemaTypeDocumentation ( schema ) ; const description = metadata . description ? `${ metadata . description }${ schemaDescription }` : schemaDescription ; return { name : normalizeName ( metadata . name , NameType . Property , true ) , description , serializedName : serializedName , type : typeName . startsWith ( "$DO_NOT_NORMALIZE$" ) ? typeName . replace ( "$DO_NOT_NORMALIZE$" , "" ) : typeName , required : ! ! required , readOnly : ! ! readOnly , nullable : ! ! nullable , isConstant , defaultValue , typeDetails , isDiscriminator : false } ; } function getObjectKind ( schema ) { if ( schema . discriminator || schema . discriminatorValue ) { return ObjectKind . Polymorphic ; } const immediateParents = getSchemaParents ( schema , true ) ; if ( immediateParents . length ) { return ObjectKind . Extended ; } return ObjectKind . Basic ; } function getObjectDetailsWithHierarchy ( objectsDetails ) { return objectsDetails . map ( current => { const parentsSchema = getSchemaParents ( current . schema , true ) ; const childrenSchema = current . schema . children && current . schema . children . immediate ; let parents = extractHierarchy ( parentsSchema , objectsDetails , current ) ; let children = extractHierarchy ( childrenSchema , objectsDetails , current ) ; const hasAdditionalProperties = Boolean ( getAdditionalProperties ( current . schema , true ) ) ; return { ... current , parents , children , hasAdditionalProperties } ; } ) ; } function extractHierarchy ( schemas , objectsDetails , current ) { if ( ! schemas || ! schemas . length ) { return [ ] ; } return schemas . map ( r => { const relativeName = normalizeName ( getLanguageMetadata ( r . language ) . name , NameType . Interface , true ) ; const relative = objectsDetails . find ( o => o . name === relativeName ) ; if ( ! relative ) { throw new Error ( `Expected relative ${ relativeName } of ${ current . name } but couldn't find it in transformed objects` ) ; } return relative ; } ) ; } function getAdditionalObjectDetails ( objectDetails , uberParents ) { switch ( objectDetails . kind ) { case ObjectKind . Basic : return objectDetails ; case ObjectKind . Polymorphic : return transformPolymorphicObject ( objectDetails as PolymorphicObjectDetails , uberParents ) ; case ObjectKind . Extended : return transformComposedObject ( objectDetails ) ; default : throw new Error ( `Unexpected ObjectKind ${ objectDetails . kind }` ) ; } } function transformComposedObject ( objectDetails ) { const immediateParents = getSchemaParents ( objectDetails . schema , true ) ; if ( immediateParents . length < 1 ) { throw new Error ( `Expected object ${ objectDetails . name } to have parents` ) ; } const parentNames = immediateParents . map ( parent => { const name = getLanguageMetadata ( parent . language ) . name ; return `${ normalizeName ( name , NameType . Interface , true ) }` ; } ) ; return { ... objectDetails , parentNames } ; } function transformPolymorphicObject ( objectDetails , uberParents ) { const schema = objectDetails . schema ; let uberParent = objectDetails . schema ; const allParents = getSchemaParents ( schema ) ; if ( allParents && allParents . length ) { const uberParentSchema = allParents . find ( p => { const name = normalizeName ( getLanguageMetadata ( p . language ) . name , NameType . Interface , true ) ; return uberParents . some ( up => up . name === name ) ; } ) ; if ( ! uberParentSchema ) { const upn = uberParents . map ( u => u . name ) ; throw new Error ( `Could not determine uberParent for Object ${ objectDetails . name }. Make sure that swagger defines polymorphism correctly; UberParents: ${ JSON . stringify ( upn ) }` ) ; } uberParent = uberParentSchema as ObjectSchema ; } const uberParentName = getLanguageMetadata ( uberParent . language ) . name ; const unionName = `${ normalizeName ( uberParentName , NameType . Interface ) }Union` ; let discriminatorPath ; let discriminatorValues = { } ; if ( schema === uberParent && schema . children ) { discriminatorPath = `${ uberParentName }` ; const discriminatorProperty = getLanguageMetadata ( uberParent . discriminator ! . property . language ) . name ; const childDiscriminators = getChildrenDiscriminators ( schema ) ; const propertyToMark = objectDetails . properties . find ( p => p . name === discriminatorProperty ) ; if ( propertyToMark ) { propertyToMark . isDiscriminator = true ; } discriminatorValues = ! childDiscriminators . length ? { } : { [ `"${ discriminatorProperty }"` ] : childDiscriminators } ; } else { discriminatorPath = `${ uberParentName }.${ schema . discriminatorValue }` ; if ( uberParent . discriminator ) { const childDiscriminators = getChildrenDiscriminators ( schema ) ; discriminatorValues = { [ getLanguageMetadata ( uberParent . discriminator . property . language ) . name ] : [ schema . discriminatorValue ! , ... childDiscriminators ] } ; } } return { ... objectDetails , discriminatorValues , discriminatorPath , unionName } as PolymorphicObjectDetails ; } function getChildrenDiscriminators ( objectSchema ) { const children = objectSchema . children ?. all ?? [ ] ; return children . map ( c => ( c as ObjectSchema ) . discriminatorValue ) . filter ( c => ! ! c ) as string [ ] ; }