Skip to content

Commit

Permalink
support individual Request type
Browse files Browse the repository at this point in the history
  • Loading branch information
inkognitro committed Oct 3, 2024
1 parent e116ff6 commit 5ec4b1e
Show file tree
Hide file tree
Showing 5 changed files with 499 additions and 280 deletions.
212 changes: 49 additions & 163 deletions src/oas3/codegen/endpoint.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import {CodeGenerator, DefinitionOutput, OutputPath, OutputType} from './core';
import {
ObjectSchema,
Endpoint,
RequestBody,
Parameter,
ObjectSchemaProps,
ConcreteParameterLocation,
concreteParameterLocations,
} from '@/oas3/specification';
import {Endpoint} from '@/oas3/specification';
import {
templateCreateRequestFunction,
templateRequestHandlerExecutionConfigType,
templateSimpleRequestHandlerType,
templateRequestResultType,
templateRequestType,
templateRequestPayloadType,
} from './template';
import {Context} from './generator';
import {applyObjectSchema} from './schema';
import {applyEndpointResponse} from './endpointResponse';
import {applyEndpointSchemaConstDefinition} from './endpointSchema';
import {applyRequestBodyByContentTypeMap} from './request';
import {findObjectSchemaFromLocationParameters} from './endpointPayloadUtils';
import {
applyRequestTypeDefinition,
ApplyRequestTypeDefinitionResult,
} from './request';

export const responseOutputPathPart = 'response6b3a7814';
export const requestResultOutputPathPart = 'requestResult6b3a7814';
Expand All @@ -29,9 +22,10 @@ function applyRequestResult(
codeGenerator: CodeGenerator,
schema: Endpoint,
path: OutputPath,
ctx: Context
ctx: Context,
requestTypeDefinition: DefinitionOutput
): DefinitionOutput {
const endpointResponseDefinition = applyEndpointResponse(
const responseTypeDefinition = applyEndpointResponse(
codeGenerator,
schema.responses,
[...path, responseOutputPathPart],
Expand All @@ -43,151 +37,47 @@ function applyRequestResult(
path,
createCode: () => {
const requestResultTypeName = templateRequestResultType.createName(path);
const requestTypeName = templateRequestType.createName(path);
const responseTypeName = endpointResponseDefinition.createName(path);
const requestTypeName = requestTypeDefinition.createName(path);
const responseTypeName = responseTypeDefinition.createName(path);
return `${requestResultTypeName}<${requestTypeName}, ${responseTypeName}>`;
},
createName: referencingPath => {
return codeGenerator.createTypeName(path, referencingPath);
},
getRequiredOutputPaths: () => [
requestTypeDefinition.path,
responseTypeDefinition.path,
templateRequestResultType.path,
endpointResponseDefinition.path,
],
};
codeGenerator.addOutput(typeDefinition, ctx);
return typeDefinition;
}

function applyNullablePayloadTypeDefinition(
codeGenerator: CodeGenerator,
nullableRequestParamsObjectSchema: null | ObjectSchema,
nullableBodyTypeDefinition: null | DefinitionOutput,
path: OutputPath,
ctx: Context
): null | DefinitionOutput {
if (!nullableRequestParamsObjectSchema && !nullableBodyTypeDefinition) {
return null;
}
const payloadPropsCodeOutput = nullableRequestParamsObjectSchema
? applyObjectSchema(
codeGenerator,
nullableRequestParamsObjectSchema,
path,
ctx
)
: null;
const payloadTypeDefinition: DefinitionOutput = {
type: OutputType.DEFINITION,
createName: referencingPath =>
codeGenerator.createTypeName(path, referencingPath),
definitionType: 'type',
path,
createCode: () => {
const codeParts = [];
if (nullableBodyTypeDefinition) {
codeParts.push(nullableBodyTypeDefinition.createName(path));
}
if (payloadPropsCodeOutput) {
codeParts.push(payloadPropsCodeOutput.createCode(path));
}
return codeParts.join(' & ');
},
getRequiredOutputPaths: () => {
const outputs = [
...(payloadPropsCodeOutput
? payloadPropsCodeOutput.getRequiredOutputPaths()
: []),
];
if (nullableBodyTypeDefinition) {
outputs.push(nullableBodyTypeDefinition.path);
}
return outputs;
},
};
codeGenerator.addOutput(payloadTypeDefinition, ctx);
return payloadTypeDefinition;
}

function createRequestCreationCode(
path: OutputPath,
endpointSchemaDefinition: DefinitionOutput,
hasPayload: boolean
): string {
const codeParts: string[] = [];
codeParts.push(endpointSchemaDefinition.createName(path));
if (hasPayload) {
codeParts.push('...payload');
codeParts.push('payload');
}
codeParts.push(
`endpointSchema: ${endpointSchemaDefinition.createName(path)}`
);
return `createRequest({${codeParts.join(',\n')}})`;
return `${templateCreateRequestFunction.createName(path)}(${codeParts.join(',\n')})`;
}

function applyNullableRequestBodyTypeDefinition(
codeGenerator: CodeGenerator,
schema: RequestBody,
function findPayloadParamCode(
path: OutputPath,
ctx: Context
): null | DefinitionOutput {
if (!schema.content) {
applyRequestResult: ApplyRequestTypeDefinitionResult
): null | string {
const fields = applyRequestResult.payloadFields;
if (!fields.length) {
return null;
}
const definitionOutput: DefinitionOutput = {
...applyRequestBodyByContentTypeMap(
codeGenerator,
schema.content,
path,
ctx
),
type: OutputType.DEFINITION,
definitionType: 'type',
path,
createName: referencingPath =>
codeGenerator.createTypeName(path, referencingPath),
};
codeGenerator.addOutput(definitionOutput, ctx);
return definitionOutput;
}

function findRequestParamsObjectSchema(
codeGenerator: CodeGenerator,
parameterSchemas: Parameter[]
): null | ObjectSchema {
const objectSchemaProps: ObjectSchemaProps = {};
const requiredObjectSchemaPropNames: string[] = [];
let hasAnyParams = false;
const payloadPropNameByParamLocation: {
[key in ConcreteParameterLocation]: string;
} = {
header: 'headers',
cookie: 'cookies',
path: 'pathParams',
query: 'queryParams',
};
concreteParameterLocations.forEach(paramLocation => {
const objectSchema = findObjectSchemaFromLocationParameters(
codeGenerator,
parameterSchemas ?? [],
paramLocation
);
if (!objectSchema) {
return;
}
hasAnyParams = true;
const propName = payloadPropNameByParamLocation[paramLocation];
objectSchemaProps[propName] = objectSchema;
if (propName !== 'cookies') {
requiredObjectSchemaPropNames.push(propName);
}
});
return !hasAnyParams
? null
: {
type: 'object',
required: requiredObjectSchemaPropNames,
properties: objectSchemaProps,
};
const requestPayloadTypeName = templateRequestPayloadType.createName(path);
const requestTypeName = applyRequestResult.typeDefinition.createName(path);
return `payload: ${requestPayloadTypeName}<${requestTypeName}, '${fields.join("' | '")}'>`;
}

export function applyEndpointCallerFunction(
Expand Down Expand Up @@ -216,30 +106,23 @@ export function applyEndpointCallerFunction(
[...path, 'endpointSchema'],
localCtx
);
const requestParamsObjectSchema = findRequestParamsObjectSchema(
const applyRequestTypeDefinitionResult = applyRequestTypeDefinition(
codeGenerator,
schema.parameters ?? []
);
const requestBodyTypeDefinition = schema.requestBody
? applyNullableRequestBodyTypeDefinition(
codeGenerator,
schema.requestBody,
[...path, 'requestBody'],
localCtx
)
: null;
const payloadTypeDefinition = applyNullablePayloadTypeDefinition(
codeGenerator,
requestParamsObjectSchema,
requestBodyTypeDefinition,
[...path, 'payload'],
schema,
[...path, 'request'],
localCtx
);
const requestTypeDefinition = applyRequestTypeDefinitionResult.typeDefinition;
const requestResultTypeDefinition = applyRequestResult(
codeGenerator,
schema,
[...path, requestResultOutputPathPart],
localCtx
localCtx,
requestTypeDefinition
);
const payloadParamCode = findPayloadParamCode(
path,
applyRequestTypeDefinitionResult
);
codeGenerator.addOutput(
{
Expand All @@ -249,36 +132,39 @@ export function applyEndpointCallerFunction(
return codeGenerator.createFunctionName(path, referencingPath);
},
createCode: () => {
const rhTn = templateSimpleRequestHandlerType.createName(path);
const pTn = payloadTypeDefinition?.createName(path);
const payloadParamCode = pTn ? `, payload: ${pTn}` : '';
const rrTn = requestResultTypeDefinition.createName(path);
const cfgTn =
templateRequestHandlerExecutionConfigType.createName(path);
const paramCodeParts = [
`requestHandler: ${templateSimpleRequestHandlerType.createName(path)}`,
];
if (payloadParamCode) {
paramCodeParts.push(payloadParamCode);
}
paramCodeParts.push(
`config?: ${templateRequestHandlerExecutionConfigType.createName(path)}`
);
const bodyParts: string[] = [];
const requestCreationCode = createRequestCreationCode(
path,
endpointSchemaConstDefinition,
!!payloadTypeDefinition
!!payloadParamCode
);
bodyParts.push(
`return requestHandler.execute(${requestCreationCode}, config);`
);
const funcBody = bodyParts.join('\n');
return `(requestHandler: ${rhTn}${payloadParamCode}, config?: ${cfgTn}): Promise<${rrTn}> {${funcBody}}`;
const bodyCode = bodyParts.join('\n');
return `(${paramCodeParts.join(', ')}): Promise<${requestResultTypeDefinition.createName(path)}> {${bodyCode}}`;
},
path,
getRequiredOutputPaths: () => {
const outputPaths: OutputPath[] = [
endpointSchemaConstDefinition.path,
templateRequestType.path,
requestTypeDefinition.path,
requestResultTypeDefinition.path,
templateSimpleRequestHandlerType.path,
templateCreateRequestFunction.path,
templateRequestHandlerExecutionConfigType.path,
];
if (payloadTypeDefinition) {
outputPaths.push(payloadTypeDefinition.path);
if (payloadParamCode) {
outputPaths.push(templateRequestPayloadType.path);
}
return outputPaths;
},
Expand Down
2 changes: 1 addition & 1 deletion src/oas3/codegen/endpointSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '@/oas3/specification';
import {applyZodSchema} from './zodSchema';
import {applyResponseSchema} from './responseSchema';
import {findObjectSchemaFromLocationParameters} from './endpointPayloadUtils';
import {findObjectSchemaFromLocationParameters} from './endpointUtils';

type ApplyRequestBodyResult = {
contentType: string;
Expand Down
File renamed without changes.
Loading

0 comments on commit 5ec4b1e

Please sign in to comment.