Skip to content

Commit

Permalink
feat: restful style openapi spec generation (zenstackhq#410)
Browse files Browse the repository at this point in the history
  • Loading branch information
ymc9 authored May 12, 2023
1 parent f07ccdd commit 4ebaa1f
Show file tree
Hide file tree
Showing 14 changed files with 6,089 additions and 100 deletions.
2 changes: 2 additions & 0 deletions packages/plugins/openapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@
"@readme/openapi-parser": "^2.4.0",
"@types/jest": "^29.5.0",
"@types/lower-case-first": "^1.0.1",
"@types/pluralize": "^0.0.29",
"@types/tmp": "^0.2.3",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"@zenstackhq/testtools": "workspace:*",
"copyfiles": "^2.4.1",
"eslint": "^8.35.0",
"jest": "^29.5.0",
"pluralize": "^8.0.0",
"rimraf": "^3.0.2",
"tmp": "^0.2.1",
"ts-jest": "^29.0.5",
Expand Down
60 changes: 60 additions & 0 deletions packages/plugins/openapi/src/generator-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { DMMF } from '@prisma/generator-helper';
import { PluginError, PluginOptions, getDataModels, hasAttribute } from '@zenstackhq/sdk';
import { Model } from '@zenstackhq/sdk/ast';
import type { OpenAPIV3_1 as OAPI } from 'openapi-types';
import { SecuritySchemesSchema } from './schema';
import { fromZodError } from 'zod-validation-error';

export abstract class OpenAPIGeneratorBase {
constructor(protected model: Model, protected options: PluginOptions, protected dmmf: DMMF.Document) {}

abstract generate(): string[];

protected get includedModels() {
return getDataModels(this.model).filter((d) => !hasAttribute(d, '@@openapi.ignore'));
}

protected wrapArray(
schema: OAPI.ReferenceObject | OAPI.SchemaObject,
isArray: boolean
): OAPI.ReferenceObject | OAPI.SchemaObject {
if (isArray) {
return { type: 'array', items: schema };
} else {
return schema;
}
}

protected array(itemType: OAPI.SchemaObject | OAPI.ReferenceObject) {
return { type: 'array', items: itemType } as const;
}

protected oneOf(...schemas: (OAPI.SchemaObject | OAPI.ReferenceObject)[]) {
return { oneOf: schemas };
}

protected allOf(...schemas: (OAPI.SchemaObject | OAPI.ReferenceObject)[]) {
return { allOf: schemas };
}

protected getOption<T = string>(name: string): T | undefined;
protected getOption<T = string, D extends T = T>(name: string, defaultValue: D): T;
protected getOption<T = string>(name: string, defaultValue?: T): T | undefined {
const value = this.options[name];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
return value === undefined ? defaultValue : value;
}

protected generateSecuritySchemes() {
const securitySchemes = this.getOption<Record<string, string>[]>('securitySchemes');
if (securitySchemes) {
const parsed = SecuritySchemesSchema.safeParse(securitySchemes);
if (!parsed.success) {
throw new PluginError(`"securitySchemes" option is invalid: ${fromZodError(parsed.error)}`);
}
return parsed.data;
}
return undefined;
}
}
10 changes: 8 additions & 2 deletions packages/plugins/openapi/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { DMMF } from '@prisma/generator-helper';
import { PluginOptions } from '@zenstackhq/sdk';
import { Model } from '@zenstackhq/sdk/ast';
import { OpenAPIGenerator } from './generator';
import { RESTfulOpenAPIGenerator } from './rest-generator';
import { RPCOpenAPIGenerator } from './rpc-generator';

export const name = 'OpenAPI';

export default async function run(model: Model, options: PluginOptions, dmmf: DMMF.Document) {
return new OpenAPIGenerator(model, options, dmmf).generate();
const flavor = options.flavor ? (options.flavor as string) : 'restful';
if (flavor === 'restful') {
return new RESTfulOpenAPIGenerator(model, options, dmmf).generate();
} else {
return new RPCOpenAPIGenerator(model, options, dmmf).generate();
}
}
Loading

0 comments on commit 4ebaa1f

Please sign in to comment.