Skip to content

Commit

Permalink
feat(openapi-generator): add initial opt-in functionality for generat…
Browse files Browse the repository at this point in the history
…ing routes
  • Loading branch information
ad-world committed May 17, 2024
1 parent 752863f commit ccf5290
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/io-ts-http/src/httpRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type HttpRoute<M extends Method = Method> = {
readonly method: Uppercase<M>;
readonly request: HttpRequestCodec<any>;
readonly response: HttpResponse;
readonly generate?: boolean;
};

export type RequestType<T extends HttpRoute> = t.TypeOf<T['request']>;
Expand Down
11 changes: 9 additions & 2 deletions packages/openapi-generator/src/apiSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Block } from 'comment-parser';
import { parsePlainInitializer } from './codec';
import type { Project } from './project';
import { resolveLiteralOrIdentifier } from './resolveInit';
import { parseRoute, type Route } from './route';
import { RouteWithGenerate, parseRoute, type Route } from './route';
import { SourceFile } from './sourceFile';
import { OpenAPIV3 } from 'openapi-types';

Expand Down Expand Up @@ -79,7 +79,14 @@ export function parseApiSpec(
if (E.isLeft(routeResultE)) {
return routeResultE;
}
result.push(routeResultE.right);

const stripRouteWithGenerate = (route: RouteWithGenerate): Route => {
const { generate, ...rest } = route;
return rest;
};

if (routeResultE.right.generate)
result.push(stripRouteWithGenerate(routeResultE.right));
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-generator/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export { convertRoutesToOpenAPI } from './openapi';
export { optimize } from './optimize';
export { Project } from './project';
export { getRefs } from './ref';
export { parseRoute, type Route } from './route';
export { parseRoute, type Route, type RouteWithGenerate } from './route';
export { parseSource } from './sourceFile';
export { parseTopLevelSymbols } from './symbol';
export type { Schema } from './ir';
28 changes: 27 additions & 1 deletion packages/openapi-generator/src/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export type Route = {
comment?: Block;
};

export type RouteWithGenerate = Route & {
generate: boolean;
};

type Request = {
parameters: Parameter[];
body?: Schema;
Expand Down Expand Up @@ -202,7 +206,22 @@ function parseRequestSchema(
}
}

export function parseRoute(project: Project, schema: Schema): E.Either<string, Route> {
function parseGenerateField(schema: Schema | undefined): E.Either<string, boolean> {
if (schema === undefined) {
return E.right(false);
}

if (schema.type !== 'boolean') {
return E.left('Route generate field must be a boolean');
}

return E.right(schema.enum![0] as boolean);
}

export function parseRoute(
project: Project,
schema: Schema,
): E.Either<string, RouteWithGenerate> {
if (schema.type !== 'object') {
return E.left('Route must be an object');
}
Expand Down Expand Up @@ -235,6 +254,12 @@ export function parseRoute(project: Project, schema: Schema): E.Either<string, R
}
const { parameters, body } = requestPropertiesE.right;

const generateField = schema.properties['generate'];
const generateFieldE = parseGenerateField(generateField);
if (E.isLeft(generateFieldE)) {
return generateFieldE;
}

if (schema.properties['response'] === undefined) {
return E.left('Route must have responses');
} else if (schema.properties['response'].type !== 'object') {
Expand All @@ -246,6 +271,7 @@ export function parseRoute(project: Project, schema: Schema): E.Either<string, R
method: schema.properties['method'].enum![0] as string,
parameters,
response: schema.properties['response'].properties,
generate: generateFieldE.right,
...(body !== undefined ? { body } : {}),
...(schema.comment !== undefined ? { comment: schema.comment } : {}),
});
Expand Down
7 changes: 7 additions & 0 deletions packages/openapi-generator/test/apiSpec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const SIMPLE = {
response: {
200: t.string,
},
generate: true
})
}
});`,
Expand Down Expand Up @@ -96,6 +97,7 @@ const ROUTE_REF = {
response: {
200: t.string,
},
generate: true
});
export const test = h.apiSpec({
Expand Down Expand Up @@ -129,6 +131,7 @@ const ACTION_REF = {
response: {
200: t.string,
},
generate: true
}),
};
Expand Down Expand Up @@ -169,6 +172,7 @@ const SPREAD = {
response: {
200: t.string,
},
generate: true
})
}
};
Expand Down Expand Up @@ -204,6 +208,7 @@ const COMPUTED_PROPERTY = {
response: {
200: t.string,
},
generate: true
})
}
});`,
Expand Down Expand Up @@ -238,6 +243,7 @@ const BASE_URL = {
response: {
200: t.string,
},
generate: true
})
}
});`,
Expand Down Expand Up @@ -282,6 +288,7 @@ const MISSING_REFERENCE = {
response: {
200: t.string,
},
generate: true
})
}
});`,
Expand Down
28 changes: 26 additions & 2 deletions packages/openapi-generator/test/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import assert from 'node:assert/strict';
import test from 'node:test';

import { TestProject } from './testProject';
import { parseRoute, parsePlainInitializer, type Route } from '../src';
import {
parseRoute,
parsePlainInitializer,
type Route,
type RouteWithGenerate,
} from '../src';

async function testCase(
description: string,
src: string,
expected: Record<string, Route>,
expected: Record<string, RouteWithGenerate>,
expectedErrors: string[] = [],
) {
test(description, async () => {
Expand Down Expand Up @@ -71,6 +76,7 @@ export const route = h.httpRoute({
response: {
200: t.string
},
generate: true
});
`;

Expand All @@ -93,6 +99,7 @@ testCase('simple route', SIMPLE, {
type: 'string',
},
},
generate: true,
},
});

Expand All @@ -110,6 +117,7 @@ export const route = h.httpRoute({
response: {
200: t.type({}),
},
generate: true
});
`;

Expand All @@ -134,6 +142,7 @@ testCase('path params route', PATH_PARAMS, {
required: [],
},
},
generate: true,
},
});

Expand Down Expand Up @@ -174,6 +183,7 @@ testCase('optional query param route', OPTIONAL_QUERY_PARAM, {
type: 'string',
},
},
generate: false,
},
});

Expand All @@ -190,6 +200,7 @@ export const route = h.httpRoute({
response: {
200: t.string
},
generate: true
});
`;

Expand All @@ -203,6 +214,7 @@ testCase('const request route', REQUEST_REF, {
type: 'string',
},
},
generate: true,
},
});

Expand All @@ -219,6 +231,7 @@ export const route = h.httpRoute({
method: 'GET',
request: h.httpRequest({}),
response: responses,
generate: true
});
`;

Expand All @@ -230,6 +243,7 @@ testCase('const response route', RESPONSE_REF, {
response: {
200: { type: 'string' },
},
generate: true,
},
});

Expand All @@ -254,6 +268,7 @@ export const route = h.httpRoute({
response: {
200: t.string
},
generate: true
});
`;

Expand Down Expand Up @@ -291,6 +306,7 @@ testCase('query param union route', QUERY_PARAM_UNION, {
response: {
200: { type: 'string' },
},
generate: true,
},
});

Expand Down Expand Up @@ -321,6 +337,7 @@ export const route = h.httpRoute({
response: {
200: t.string
},
generate: true
});
`;

Expand Down Expand Up @@ -364,6 +381,7 @@ testCase('path param union route', PATH_PARAM_UNION, {
response: {
200: { type: 'string' },
},
generate: true,
},
});

Expand All @@ -388,6 +406,7 @@ export const route = h.httpRoute({
response: {
200: t.string
},
generate: true
});
`;

Expand Down Expand Up @@ -418,6 +437,7 @@ testCase('body union route', BODY_UNION, {
response: {
200: { type: 'string' },
},
generate: true,
},
});

Expand All @@ -442,6 +462,7 @@ export const route = h.httpRoute({
response: {
200: t.string
},
generate: true
});
`;

Expand All @@ -466,6 +487,7 @@ testCase('request intersection route', REQUEST_INTERSECTION, {
response: {
200: { type: 'string' },
},
generate: true,
},
});

Expand Down Expand Up @@ -520,6 +542,7 @@ testCase('request body intersection route', BODY_INTERSECTION, {
response: {
200: { type: 'string' },
},
generate: false,
},
});

Expand Down Expand Up @@ -550,6 +573,7 @@ testCase('route with operationId', WITH_OPERATION_ID, {
route: {
path: '/foo',
method: 'GET',
generate: false,
parameters: [
{
type: 'query',
Expand Down

0 comments on commit ccf5290

Please sign in to comment.