Skip to content

Commit

Permalink
Merge pull request #175 from pas-mike/typegen-path-parameters-require…
Browse files Browse the repository at this point in the history
…d-in-ts

refactor(typegen): path parameters required in output
  • Loading branch information
anttiviljami authored Jan 29, 2024
2 parents 0c23f67 + 7d6bcef commit bc6ae54
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 6 deletions.
10 changes: 10 additions & 0 deletions packages/typegen/src/typegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('typegen', () => {
beforeAll(async () => {
const types = await generateTypesForDocument(examplePetAPIYAML, {
transformOperationName: (operationId: string) => operationId,
disableOptionalPathParameters: true,
});
imports = types[0];
schemaTypes = types[1];
Expand Down Expand Up @@ -38,6 +39,15 @@ describe('typegen', () => {
expect(operationTypings).toMatch('getPetsRelative');
});

test('types parameters', () => {
expect(operationTypings).toMatch(`parameters: Parameters<Paths.GetPetById.PathParameters>`);
expect(operationTypings).toMatch(`parameters: Parameters<Paths.ReplacePetById.PathParameters>`);
expect(operationTypings).toMatch(`parameters: Parameters<Paths.UpdatePetById.PathParameters>`);
expect(operationTypings).toMatch(`parameters: Parameters<Paths.DeletePetById.PathParameters>`);
expect(operationTypings).toMatch(`parameters: Parameters<Paths.GetOwnerByPetId.PathParameters>`);
expect(operationTypings).toMatch(`parameters: Parameters<Paths.GetPetOwner.PathParameters>`);
});

test('types responses', () => {
expect(operationTypings).toMatch(`OperationResponse<Paths.GetPets.Responses.$200>`);
expect(operationTypings).toMatch('OperationResponse<Paths.CreatePet.Responses.$201>');
Expand Down
37 changes: 31 additions & 6 deletions packages/typegen/src/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { JSONSchema } from '@apidevtools/json-schema-ref-parser/dist/lib/types';

interface TypegenOptions {
transformOperationName?: (operation: string) => string;
disableOptionalPathParameters?: boolean;
}

interface ExportedType {
Expand All @@ -36,6 +37,11 @@ export async function main() {
alias: 't',
type: 'string',
})
.option('disableOptionalPathParameters', {
type: 'string',
description: 'If true the parameters will be required when path parameters are given',
default: false,
})
.usage('Usage: $0 [file]')
.example('$0 ./openapi.yml > openapi.d.ts', '')
.example('$0 https://openapistack.co/petstore.openapi.json > openapi.d.ts', '')
Expand All @@ -61,6 +67,8 @@ export async function main() {
opts.transformOperationName = module[func];
}

opts.disableOptionalPathParameters = argv.disableOptionalPathParameters ?? false;

const [imports, schemaTypes, operationTypings] = await generateTypesForDocument(argv._[0] as string, opts);
console.log(imports, '\n');
console.log(schemaTypes);
Expand Down Expand Up @@ -99,15 +107,27 @@ export async function generateTypesForDocument(definition: Document | string, op
return [imports, schemaTypes, operationTypings];
}

function generateMethodForOperation(methodName: string, operation: Operation, exportTypes: ExportedType[]) {
function generateMethodForOperation(
methodName: string,
operation: Operation,
exportTypes: ExportedType[],
opts: TypegenOptions,
) {
const { operationId, summary, description } = operation;

// parameters arg
const normalizedOperationId = convertKeyToTypeName(operationId);
const normalizedPath = convertKeyToTypeName(operation.path);
const parameterTypePaths = _.chain([

const pathParameterTypePaths = _.chain([
_.find(exportTypes, { schemaRef: `#/paths/${normalizedOperationId}/pathParameters` }),
_.find(exportTypes, { schemaRef: `#/paths/${normalizedPath}/pathParameters` }),
])
.filter()
.map('path')
.value();

const parameterTypePaths = _.chain([
_.find(exportTypes, { schemaRef: `#/paths/${normalizedOperationId}/queryParameters` }),
_.find(exportTypes, { schemaRef: `#/paths/${normalizedPath}/queryParameters` }),
_.find(exportTypes, { schemaRef: `#/paths/${normalizedOperationId}/headerParameters` }),
Expand All @@ -117,10 +137,15 @@ function generateMethodForOperation(methodName: string, operation: Operation, ex
])
.filter()
.map('path')
.value();
.value()
.concat(pathParameterTypePaths);

const parametersType = !_.isEmpty(parameterTypePaths) ? parameterTypePaths.join(' & ') : 'UnknownParamsObject';
const parametersArg = `parameters?: Parameters<${parametersType}> | null`;
let parametersArg = `parameters?: Parameters<${parametersType}> | null`;

if (opts.disableOptionalPathParameters && !_.isEmpty(pathParameterTypePaths)) {
parametersArg = `parameters: Parameters<${parametersType}>`;
}

// payload arg
const requestBodyType = _.find(exportTypes, { schemaRef: `#/paths/${normalizedOperationId}/requestBody` });
Expand Down Expand Up @@ -162,7 +187,7 @@ export function generateOperationMethodTypings(
const operationTypings = operations
.map((op) => {
return op.operationId
? generateMethodForOperation(opts.transformOperationName(op.operationId), op, exportTypes)
? generateMethodForOperation(opts.transformOperationName(op.operationId), op, exportTypes, opts)
: null;
})
.filter((op) => Boolean(op));
Expand All @@ -174,7 +199,7 @@ export function generateOperationMethodTypings(
const method = m as HttpMethod;
const operation = _.find(operations, { path, method });
if (operation.operationId) {
const methodForOperation = generateMethodForOperation(method, operation, exportTypes);
const methodForOperation = generateMethodForOperation(method, operation, exportTypes, opts);
methodTypings.push(methodForOperation);
}
}
Expand Down

0 comments on commit bc6ae54

Please sign in to comment.