Skip to content

Commit 4636923

Browse files
authored
Merge pull request #15 from povio/feature/enums
Enum extraction, Check for inline enums & Minor refactoring
2 parents 5553b46 + 40c0bbe commit 4636923

58 files changed

Lines changed: 1506 additions & 1142 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,32 @@ yarn openapi-codegen generate --input http://localhost:3001/docs-json
2020

2121
## Options
2222

23-
### Generate command
23+
### Generate command (generates Zod schemas, API definitions and React queries)
2424

2525
```sh
26-
--input <path> Path/url to OpenAPI/Swagger document as json/yaml
27-
--output <path> Output path (default: 'output')
26+
--input Path/url to OpenAPI/Swagger document as json/yaml
27+
--output Output path (default: 'output')
2828
--includeNamespaces Include namespaces inside generated files (default: true)
2929
--splitByTags Split output into directories based on tags in OpenAPI operations (default: true)
3030
--defaultTag Default tag name for code shared accross multiple tags (default: 'Common')
3131
--excludeTags Comma separated list of tags excluded from the output
32+
--extractEnums Enums are extracted as seperate Zod schemas (default: true)
3233
--removeOperationPrefixEndingWith Removes prefix that ends with value from operation names (default: 'Controller_')
3334
--importPath Import path (default: 'ts', possible: 'ts' | 'relative' | 'absolute')
3435
--prettier Run prettier command on output after code generation (default: true)
3536
--verbose Show log messages during execution
3637
```
3738

39+
### Check command (checks if OpenAPI spec is compliant)
40+
41+
```sh
42+
--input Path/url to OpenAPI/Swagger document as json/yaml
43+
--splitByTags Split output into directories based on tags in OpenAPI operations (default: true)
44+
--defaultTag Default tag name for code shared accross multiple tags (default: 'Common')
45+
--excludeTags Comma separated list of tags excluded from the output
46+
--verbose Show log messages during execution
47+
```
48+
3849
## Development
3950

4051
### Test locally

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@povio/openapi-codegen-cli",
3-
"version": "0.3.9",
3+
"version": "0.4.0",
44
"main": "./dist/index.js",
55
"bin": {
66
"openapi-codegen": "./dist/sh.js"

src/commands/check.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export type CheckParams = {
1010
} & Pick<GenerateOptions, "input" | "splitByTags" | "defaultTag">;
1111

1212
export async function check({ input, excludeTags, verbose, ...params }: CheckParams) {
13+
const start = Date.now();
14+
1315
if (verbose) {
1416
logInfo("Parsing OpenAPI spec...");
1517
}
@@ -21,15 +23,16 @@ export async function check({ input, excludeTags, verbose, ...params }: CheckPar
2123
if (verbose) {
2224
logInfo("Running check...");
2325
}
24-
const errorMessages = checkOpenAPIDoc({
25-
openApiDoc,
26-
options: {
27-
input,
28-
excludeTags: excludeTags.split(","),
29-
...params,
30-
},
26+
const errorMessages = checkOpenAPIDoc(openApiDoc, {
27+
input,
28+
excludeTags: excludeTags.split(","),
29+
...params,
3130
});
3231

32+
if (verbose) {
33+
logInfo(`TIME: ${Date.now() - start}ms`);
34+
}
35+
3336
if (errorMessages.length === 0) {
3437
logSuccess("Check finished.");
3538
} else {

src/commands/generate.command.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ class GenerateOptions implements GenerateParams {
2626
@YargOption({ envAlias: "importPath", default: "ts", type: "string" })
2727
importPath!: "ts" | "relative" | "absolute";
2828

29+
@YargOption({ envAlias: "extractEnums", default: true, type: "boolean" })
30+
extractEnums!: boolean;
31+
2932
@YargOption({ envAlias: "removeOperationPrefixEndingWith", default: "Controller_" })
3033
removeOperationPrefixEndingWith!: string;
3134

src/commands/generate.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ export type GenerateParams = {
1818
| "defaultTag"
1919
| "removeOperationPrefixEndingWith"
2020
| "importPath"
21+
| "extractEnums"
2122
>;
2223

2324
export async function generate({ input, excludeTags, prettier, verbose, ...params }: GenerateParams) {
25+
const start = Date.now();
26+
2427
if (verbose) {
2528
logInfo("Parsing OpenAPI spec...");
2629
}
@@ -32,13 +35,10 @@ export async function generate({ input, excludeTags, prettier, verbose, ...param
3235
if (verbose) {
3336
logInfo("Generating code...");
3437
}
35-
generateCodeFromOpenAPIDoc({
36-
openApiDoc,
37-
options: {
38-
input,
39-
excludeTags: excludeTags.split(","),
40-
...params,
41-
},
38+
generateCodeFromOpenAPIDoc(openApiDoc, {
39+
input,
40+
excludeTags: excludeTags.split(","),
41+
...params,
4242
});
4343
if (verbose) {
4444
logSuccess("Generation finished.");
@@ -47,6 +47,10 @@ export async function generate({ input, excludeTags, prettier, verbose, ...param
4747
if (prettier) {
4848
execPrettier({ output: params.output, verbose });
4949
}
50+
51+
if (verbose) {
52+
logInfo(`TIME: ${Date.now() - start}ms`);
53+
}
5054
}
5155

5256
function execPrettier({ output, verbose }: Pick<GenerateParams, "output" | "verbose">) {

src/generators/checkOpenAPIDoc.ts

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,40 @@
11
import { OpenAPIV3 } from "openapi-types";
2-
import { logInfo } from "src/helpers/cli.helper";
2+
import { chk } from "src/helpers/chalk.helper";
33
import { DEFAULT_GENERATE_OPTIONS } from "./const/options.const";
4+
import { VALIDATION_ERROR_TYPE_TITLE } from "./const/validation.const";
45
import { getDataFromOpenAPIDoc } from "./core/getDataFromOpenAPIDoc";
56
import { GenerateType } from "./types/generate";
67
import { GenerateOptions } from "./types/options";
8+
import { ValidationErrorType } from "./types/validation";
79
import { getFileName } from "./utils/file.utils";
810
import { getTagFileName } from "./utils/generate/generate.utils";
11+
import { groupByType } from "./utils/validation.utils";
912

10-
export function checkOpenAPIDoc({
11-
openApiDoc,
12-
options: cliOptions,
13-
}: {
14-
openApiDoc: OpenAPIV3.Document;
15-
options?: Partial<GenerateOptions>;
16-
}) {
13+
export function checkOpenAPIDoc(openApiDoc: OpenAPIV3.Document, cliOptions?: Partial<GenerateOptions>) {
1714
const options = { ...DEFAULT_GENERATE_OPTIONS, ...cliOptions } as GenerateOptions;
1815

19-
const { resolver, data, validationErrorMessages } = getDataFromOpenAPIDoc({ openApiDoc, options });
16+
const { resolver, data } = getDataFromOpenAPIDoc(openApiDoc, options);
2017

21-
const errorMessages = [...validationErrorMessages, ...resolver.validationErrorMessages];
22-
if (errorMessages.length > 0) {
23-
logInfo(`Issues:\n${errorMessages.map((message) => `- ${message}`).join("\n")}`);
18+
if (resolver.validationErrors.length > 0) {
19+
const groupedErrors = groupByType(resolver.validationErrors);
20+
Object.entries(groupedErrors).forEach(([type, errorMessages]) => {
21+
console.log(
22+
`${chk.red(`${VALIDATION_ERROR_TYPE_TITLE[type as ValidationErrorType]}:`)}\n${errorMessages.map((message) => `- ${message}`).join("\n")}\n`,
23+
);
24+
});
2425
} else {
2526
const outputs = [...data.keys()].reduce((acc, tag) => {
26-
const excludedTagIndex = options.excludeTags?.findIndex(
27-
(excludeTag) => excludeTag.toLocaleLowerCase() === tag.toLocaleLowerCase(),
28-
);
29-
const isExcludedTag = excludedTagIndex !== -1;
30-
if (isExcludedTag) {
31-
return acc;
32-
}
33-
34-
return [
35-
...acc,
36-
...[GenerateType.Models, GenerateType.Endpoints, GenerateType.Queries].map((type) =>
37-
getFileName({ output: options.output, fileName: getTagFileName({ tag, type, options }) }),
38-
),
39-
];
27+
const excludedTag = options.excludeTags.find((excludeTag) => excludeTag.toLowerCase() === tag.toLowerCase());
28+
return excludedTag ? acc : [...acc, ...getTagOutputFileNames(tag, options)];
4029
}, [] as string[]);
41-
logInfo(`Output:\n${outputs.map((message) => `- ${message}`).join("\n")}`);
30+
console.log(`${chk.green("Outputs:")}\n${outputs.map((output) => `- ${output}`).join("\n")}\n`);
4231
}
4332

44-
return errorMessages;
33+
return resolver.validationErrors;
34+
}
35+
36+
function getTagOutputFileNames(tag: string, options: GenerateOptions) {
37+
return [GenerateType.Models, GenerateType.Endpoints, GenerateType.Queries].map((type) =>
38+
getFileName({ output: options.output, fileName: getTagFileName({ tag, type, options }) }),
39+
);
4540
}

src/generators/const/options.const.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { GenerateType } from "../types/generate";
22
import { GenerateOptions } from "../types/options";
3-
import { SCHEMA_SUFFIX } from "./zod.const";
3+
import { ENUM_SUFFIX, SCHEMA_SUFFIX } from "./zod.const";
44

55
export const DEFAULT_GENERATE_OPTIONS: GenerateOptions = {
66
// Base options
@@ -11,6 +11,7 @@ export const DEFAULT_GENERATE_OPTIONS: GenerateOptions = {
1111
excludeTags: [], // TODO: Only works for isolated tags
1212
includeNamespaces: true,
1313
importPath: "ts",
14+
extractEnums: true,
1415
configs: {
1516
[GenerateType.Models]: {
1617
outputFileNameSuffix: "models",
@@ -27,6 +28,7 @@ export const DEFAULT_GENERATE_OPTIONS: GenerateOptions = {
2728
},
2829
// Zod options
2930
schemaSuffix: SCHEMA_SUFFIX,
31+
enumSuffix: ENUM_SUFFIX,
3032
additionalPropertiesDefaultValue: false,
3133
// Endpoints options
3234
removeOperationPrefixEndingWith: "Controller_",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ValidationErrorType } from "../types/validation";
2+
3+
export const VALIDATION_ERROR_TYPE_TITLE: Record<ValidationErrorType, string> = {
4+
"invalid-schema": "Invalid Schemas",
5+
"invalid-operation-id": "Invalid Operation IDs",
6+
"missing-path-parameter": "Missing Path Parameters",
7+
"not-allowed-inline-enum": "Not Allowed Inline Enums",
8+
};

src/generators/const/zod.const.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Import } from "../types/generate";
22

33
export const SCHEMA_SUFFIX = "Schema";
4+
export const ENUM_SUFFIX = "Enum";
45
export const BODY_SCHEMA_SUFFIX = "Body";
56
export const PARAM_SCHEMA_SUFFIX = "Param";
67
export const RESPONSE_SCHEMA_SUFFIX = "Response";

0 commit comments

Comments
 (0)