diff --git a/src/generator/01-base/static/queriesWithFilter.ts.txt b/src/generator/01-base/static/queriesWithFilter.ts.txt index 263c086..b5cc578 100644 --- a/src/generator/01-base/static/queriesWithFilter.ts.txt +++ b/src/generator/01-base/static/queriesWithFilter.ts.txt @@ -37,7 +37,7 @@ export type CountQuery = { export type SomeQuery = { serializeNulls?: boolean; - include?: QuerySelect; + include?: I; properties?: P; filter?: QueryFilter & CustomAttributeFilter; select?: QuerySelect; @@ -178,7 +178,7 @@ const _some = ( includeReferencedEntities: query?.include ? usePost ? Object.keys(query.include) - : Object.keys(query.include).join(',') + : flattenIncludedReferences(query.include).join(',') : undefined, ...flattenOrFilter(query?.or), ...flattenFilter(query?.filter), diff --git a/src/generator/01-base/static/queriesWithQueryLanguage.ts.txt b/src/generator/01-base/static/queriesWithQueryLanguage.ts.txt index 028c5d6..fb90dc8 100644 --- a/src/generator/01-base/static/queriesWithQueryLanguage.ts.txt +++ b/src/generator/01-base/static/queriesWithQueryLanguage.ts.txt @@ -163,7 +163,7 @@ export type CountQuery = { type SomeQueryBase = { serializeNulls?: boolean; - include?: QuerySelect; + include?: I; properties?: P; where?: QueryFilter; select?: QuerySelect; @@ -387,7 +387,7 @@ const _some = ( includeReferencedEntities: query?.include ? usePost ? Object.keys(query.include) - : Object.keys(query.include).join(',') + : flattenIncludedReferences(query.include).join(',') : undefined, ...assembleFilterParam(query?.where), ...flattenSort(query?.sort, usePost), diff --git a/src/generator/01-base/static/types.ts.txt b/src/generator/01-base/static/types.ts.txt index 56986e4..2e5e7e8 100644 --- a/src/generator/01-base/static/types.ts.txt +++ b/src/generator/01-base/static/types.ts.txt @@ -62,9 +62,9 @@ export type UniqueQuery = { serializeNulls?: boolean; }; -export type SomeQueryReturn = { +export type SomeQueryReturn = { entities: E[]; - references?: R; + references?: ReferencedEntities; properties?: P[]; }; diff --git a/src/generator/01-base/static/utils.ts.txt b/src/generator/01-base/static/utils.ts.txt index 9cdba28..cdc4b97 100644 --- a/src/generator/01-base/static/utils.ts.txt +++ b/src/generator/01-base/static/utils.ts.txt @@ -12,6 +12,22 @@ const flattenSelect = (obj: Select = {}): string[] => { return entries; }; +const flattenIncludedReferences = (obj: Record = {}, base = ''): string[] => { + const entries: string[] = []; + + for (const [prop, value] of Object.entries(obj)) { + const path = base ? `${base}.${prop}` : prop; + + if (typeof value === 'object' && value) { + entries.push(...flattenIncludedReferences(value as Record, path)); + } else if (value) { + entries.push(path); + } + } + + return entries; +}; + export const flattenSort = (obj: Sort[] = [], usePost?: boolean): { sort?: string } | { orderBy?: string[] } | Record => { const flatten = (obj: Sort, base = ''): string | undefined => { diff --git a/src/generator/03-entities/index.ts b/src/generator/03-entities/index.ts index a85d70e..8b52b28 100644 --- a/src/generator/03-entities/index.ts +++ b/src/generator/03-entities/index.ts @@ -1,12 +1,12 @@ import { extractPropertyMetaData, PropertyMetaData } from '@generator/03-entities/utils/extractPropertyMetaData'; -import { generateInterface, InterfaceProperty } from '@ts/generateInterface'; +import { logger } from '@logger'; +import { generateInterface, generateInterfaceType, InterfaceProperty } from '@ts/generateInterface'; import { generateStatements } from '@ts/generateStatements'; import { loosePascalCase } from '@utils/case'; import { convertToTypeScriptType, createReferenceType, getRefName } from '@utils/openapi/convertToTypeScriptType'; import { ExtendedSchema, isEnumSchemaObject, isReferenceObject } from '@utils/openapi/guards'; -import { OpenAPIV3 } from 'openapi-types'; -import { logger } from '@logger'; import { OpenApiContext } from '@utils/weclapp/extractContext'; +import { OpenAPIV3 } from 'openapi-types'; export interface GeneratedEntity { name: string; @@ -144,3 +144,30 @@ export const generateEntities = (context: OpenApiContext): Map, + aliases: Map, + services: Map +) => { + const aliasKeys = new Set(aliases.keys()); + const serviceKeys = new Set(services.keys()); + + return generateInterfaceType( + 'ReferencedEntities', + [ + ...[...entities.values()] + .filter((entity) => aliasKeys.has(entity.name) && serviceKeys.has(entity.name)) + .map((entity) => ({ + name: entity.name, + type: `${entity.interfaceName}[]` + })), + ...[...aliases.entries()] + .filter(([, alias]) => alias === 'customValue') + .map(([entityName]) => ({ + name: entityName, + type: 'CustomValue[]' + })) + ].sort((a, b) => a.name.localeCompare(b.name)) + ); +}; diff --git a/src/generator/04-services/endpoints/some.ts b/src/generator/04-services/endpoints/some.ts index 0986d35..3d98888 100644 --- a/src/generator/04-services/endpoints/some.ts +++ b/src/generator/04-services/endpoints/some.ts @@ -2,6 +2,7 @@ import { GeneratedServiceFunction, ServiceFunctionGenerator } from '@generator/0 import { generateArrowFunction } from '@ts/generateArrowFunction'; import { generateArrowFunctionType } from '@ts/generateArrowFunctionType'; import { generateInterfaceFromObject, generateInterfaceType, InterfaceProperty } from '@ts/generateInterface'; +import { generateObject, ObjectProperty } from '@ts/generateObject'; import { generateString } from '@ts/generateString'; import { generateTupleArray } from '@ts/generateTupleArray'; import { generateType } from '@ts/generateType'; @@ -46,46 +47,99 @@ const resolveAdditionalPropertiesSchema = ( return undefined; }; -const resolveReferences = (entity: string, entities: Map) => { - const references: InterfaceProperty[] = []; +const resolveArrayReferenceProperties = ( + entity: string, + entities: Map, + visitedEntities = new Set() +): ObjectProperty[] => { + if (!entity || visitedEntities.has(entity)) { + return []; + } + const generatedEntity = entities.get(entity); - if (generatedEntity) { - for (const [property, propertyMetaData] of generatedEntity.properties) { - if (propertyMetaData.service) { - references.push({ - name: property, - type: generateString(propertyMetaData.service), - required: true - }); + if (!generatedEntity) { + return []; + } + + const nextVisitedEntities = new Set(visitedEntities); + nextVisitedEntities.add(entity); + + const properties: ObjectProperty[] = [...generatedEntity.properties.entries()].flatMap( + ([property, propertyMetaData]): ObjectProperty[] => { + if (propertyMetaData.type === 'array') { + if (propertyMetaData.entity === 'onlyId') { + return [{ key: property, value: [{ key: 'id', value: 'boolean' }] }]; + } + + if (!propertyMetaData.entity) { + return []; + } + + const nestedProperties = resolveArrayReferenceProperties( + propertyMetaData.entity, + entities, + nextVisitedEntities + ); + + if (!nestedProperties.length) { + return []; + } + + return [{ key: property, value: nestedProperties }]; } + + if (property.endsWith('Id')) { + return [{ key: property, value: 'boolean' }]; + } + + return []; } - if (generatedEntity.parentName) { - references.push(...resolveReferences(generatedEntity.parentName, entities)); - } + ); + + if (generatedEntity.parentName) { + properties.push(...resolveArrayReferenceProperties(generatedEntity.parentName, entities, nextVisitedEntities)); } - return references; + + return properties; }; -const resolveReferencedEntities = (entity: string, entities: Map) => { - const referencedEntities: InterfaceProperty[] = []; +const resolveReferences = (entity: string, entities: Map) => { + const references: InterfaceProperty[] = []; const generatedEntity = entities.get(entity); if (generatedEntity) { - for (const [, propertyMetaData] of generatedEntity.properties) { - if (propertyMetaData.service && propertyMetaData.entity) { - const referencedEntity = entities.get(propertyMetaData.entity); - if (referencedEntity) - referencedEntities.push({ - name: propertyMetaData.service, - type: `${referencedEntity.interfaceName}[]`, - required: true + for (const [property, propertyMetaData] of generatedEntity.properties) { + if (property === 'customAttributes') continue; + + if (propertyMetaData.type === 'array') { + if (propertyMetaData.entity === 'onlyId') { + references.push({ + name: property, + type: generateObject([{ key: 'id', value: 'boolean' }]) }); + } else { + const nestedProperties = resolveArrayReferenceProperties(propertyMetaData.entity || '', entities); + if (nestedProperties.length) { + references.push({ + name: property, + type: generateObject(nestedProperties) + }); + } + } + } else { + if (propertyMetaData.service) { + references.push({ + name: property, + type: 'boolean' + }); + } } } if (generatedEntity.parentName) { - referencedEntities.push(...resolveReferencedEntities(generatedEntity.parentName, entities)); + references.push(...resolveReferences(generatedEntity.parentName, entities)); } } - return referencedEntities; + + return references; }; export const generateSomeEndpoint: ServiceFunctionGenerator = ({ @@ -134,12 +188,6 @@ export const generateSomeEndpoint: ServiceFunctionGenerator = ({ `SomeQuery<${relatedEntity.interfaceName}, ${filterTypeName}, ${referencesTypeName}, ${additionalPropertyTypeName}> & ${parametersTypeName}` ); - const referencedEntitiesTypeName = `${functionTypeName}_ReferencedEntities`; - const referencedEntitiesTypeSource = generateInterfaceType( - referencedEntitiesTypeName, - resolveReferencedEntities(endpoint.service, entities) - ); - const additionalPropertiesTypeName = `${functionTypeName}_AdditionalProperties`; const additionalPropertiesTypeSource = generateType( @@ -150,7 +198,7 @@ export const generateSomeEndpoint: ServiceFunctionGenerator = ({ const functionTypeSource = generateArrowFunctionType({ type: functionTypeName, params: [`query${parametersType.isFullyOptional() ? '?' : ''}: ${queryTypeName}, requestOptions?: RequestOptions`], - returns: `${resolveResponseType(options.target)}>` + returns: `${resolveResponseType(options.target)}>` }); const functionSource = generateArrowFunction({ @@ -170,7 +218,6 @@ export const generateSomeEndpoint: ServiceFunctionGenerator = ({ { name: referencesTypeName, source: referencesTypeSource }, { name: additionalPropertyTypeName, source: additionalPropertyTypeSource }, { name: queryTypeName, source: queryTypeSource }, - { name: referencedEntitiesTypeName, source: referencedEntitiesTypeSource }, { name: additionalPropertiesTypeName, source: additionalPropertiesTypeSource } ] }; diff --git a/src/generator/generate.ts b/src/generator/generate.ts index 36d5a76..220a4c4 100644 --- a/src/generator/generate.ts +++ b/src/generator/generate.ts @@ -1,13 +1,13 @@ -import { Target } from '../target'; import { generateBase } from '@generator/01-base'; import { generateEnums } from '@generator/02-enums'; -import { generateEntities } from '@generator/03-entities'; +import { generateEntities, generateReferencedEntities } from '@generator/03-entities'; import { generateServices } from '@generator/04-services'; import { generateMaps } from '@generator/05-maps'; import { generateBlockComment } from '@ts/generateComment'; import { generateStatements } from '@ts/generateStatements'; import { extractContext } from '@utils/weclapp/extractContext'; import { OpenAPIV3 } from 'openapi-types'; +import { Target } from '../target'; export interface GeneratorOptions { /* Generate unique methods as well. */ @@ -34,6 +34,7 @@ export const generate = (doc: OpenAPIV3.Document, options: GeneratorOptions): st generateBlockComment('ENUMS', generateStatements(...[...enums.values()].map((v) => v.source))), generateBlockComment('ENTITIES', generateStatements(...[...entities.values()].map((v) => v.source))), generateBlockComment('FILTERS', generateStatements(...[...entities.values()].map((v) => v.filterSource))), + generateBlockComment('REFERENCED ENTITIES', generateReferencedEntities(entities, context.aliases, services)), generateBlockComment('SERVICES', generateStatements(...[...services.values()].map((v) => v.source))), generateBlockComment('MAPS', maps) );