Skip to content

Commit 639a985

Browse files
committed
feat: allow function/async function config, add more source types, make patch functions async
1 parent 15be265 commit 639a985

File tree

6 files changed

+93
-60
lines changed

6 files changed

+93
-60
lines changed

src/cli/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ async function loadConfig(filename: string): Promise<ApiTypescriptGeneratorConfi
2727
const {register} = await import('ts-node');
2828
register();
2929
}
30-
const config = await import(fullFilename);
31-
return 'default' in config ? config.default : config;
30+
const configImport = await import(fullFilename);
31+
let config = 'default' in configImport ? configImport.default : configImport;
32+
if (typeof config === 'function') {
33+
config = await config();
34+
}
35+
return config;
3236
} catch (e) {
3337
throw new Error(`Could not load configuration file: ${e instanceof Error ? e.message : e}`);
3438
}

src/schema-to-typescript/config.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ export type CommonApiToTypescriptGeneratorSource =
2626
* URL to the file.
2727
*/
2828
url: string;
29+
}
30+
| {
31+
type: 'object';
32+
/**
33+
* OpenAPI schema as an object.
34+
*/
35+
object: unknown;
36+
}
37+
| {
38+
type: 'string';
39+
/**
40+
* OpenAPI schema as a string.
41+
*/
42+
data: string;
2943
};
3044

3145
/**
@@ -44,16 +58,23 @@ export interface ApiTypescriptGeneratorConfig {
4458
* @param schema The schema to patch.
4559
* @param schemaName The name of the schema.
4660
*/
47-
export type OpenApiDocumentPatchSchema = (schema: OpenApiSchema, schemaName: string) => OpenApiSchema;
61+
export type OpenApiDocumentPatchSchema = (
62+
schema: OpenApiSchema,
63+
schemaName: string
64+
) => OpenApiSchema | Promise<OpenApiSchema>;
4865

4966
/**
5067
* Callback to patch all schemas.
5168
*
5269
* @param schemas The schemas to patch.
5370
*/
54-
export type OpenApiDocumentPatchAllSchemas = (schemas: {[schemaName: string]: OpenApiSchema}) => {
55-
[schemaName: string]: OpenApiSchema;
56-
};
71+
export type OpenApiDocumentPatchAllSchemas = (schemas: {[schemaName: string]: OpenApiSchema}) =>
72+
| {
73+
[schemaName: string]: OpenApiSchema;
74+
}
75+
| Promise<{
76+
[schemaName: string]: OpenApiSchema;
77+
}>;
5778

5879
/**
5980
* Callback to patch an operation.
@@ -66,26 +87,29 @@ export type OpenApiDocumentPatchOperation = (
6687
operation: OpenApiOperation,
6788
path: string,
6889
httpMethod: OpenApiHttpMethod
69-
) => OpenApiOperation;
90+
) => OpenApiOperation | Promise<OpenApiOperation>;
7091
/**
7192
* Callback to patch a path item.
7293
*
7394
* @param pathItem The path item to patch.
7495
* @param path The path of the path item.
7596
*/
76-
export type OpenApiDocumentPatchPathItem = (pathItem: OpenApiPathItem, path: string) => OpenApiPathItem;
97+
export type OpenApiDocumentPatchPathItem = (
98+
pathItem: OpenApiPathItem,
99+
path: string
100+
) => OpenApiPathItem | Promise<OpenApiPathItem>;
77101
/**
78102
* Callback to patch tags.
79103
*
80104
* @param tags The tags to patch.
81105
*/
82-
export type OpenApiDocumentPatchTags = (tags: OpenApiTag[]) => OpenApiTag[];
106+
export type OpenApiDocumentPatchTags = (tags: OpenApiTag[]) => OpenApiTag[] | Promise<OpenApiTag[]>;
83107
/**
84108
* Callback to patch the whole OpenAPI document. Applies after all other patches.
85109
*
86110
* @param document The OpenAPI document to patch.
87111
*/
88-
export type OpenApiDocumentPatchDocument = (document: OpenApiDocument) => OpenApiDocument;
112+
export type OpenApiDocumentPatchDocument = (document: OpenApiDocument) => OpenApiDocument | Promise<OpenApiDocument>;
89113

90114
/**
91115
* Configuration to patch an OpenAPI document.

src/schemas/fetch-source.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ export async function fetchSource(source: CommonApiToTypescriptGeneratorSource):
99
throw new Error(`Error downloading "${source.url}": ${response.statusText}`);
1010
}
1111
return yaml.load(await response.text());
12-
} else {
12+
} else if (source.type === 'file') {
1313
try {
1414
return yaml.load(await fs.promises.readFile(source.path, 'utf8'));
1515
} catch (e) {
1616
throw new Error(`Error reading file "${source.path}": ${e instanceof Error ? e.message : e}`);
1717
}
18+
} else if (source.type === 'object') {
19+
return source.object;
20+
} else if (source.type === 'string') {
21+
return yaml.load(source.data);
22+
} else {
23+
throw new Error(`Unknown source type: ${(source as {type: string}).type}`);
1824
}
1925
}

src/schemas/load-open-api-document.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export async function loadOpenApiDocument(
99
): Promise<OpenApiDocument> {
1010
let document = (await fetchSource(config.source)) as OpenApiDocument;
1111
if (config.patch) {
12-
document = patchOpenApiDocument(document, config.patch);
12+
document = await patchOpenApiDocument(document, config.patch);
1313
}
1414
document = resolveDocumentReferences(processOpenApiDocument(document));
1515
return document;

src/schemas/patch-open-api-document.ts

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import {OpenApiDocument, OpenApiHttpMethod, OpenApiOperation, OpenApiPathItem} f
33
import {CommonOpenApiClientGeneratorConfigDocumentPatch} from '../schema-to-typescript/config';
44
import {JsonObject, JsonValue} from '../utils/json-value';
55

6-
function patch<T>(document: OpenApiDocument, path: string[], patch: (data: T) => T): OpenApiDocument {
6+
async function patch<T>(
7+
document: OpenApiDocument,
8+
path: string[],
9+
patch: (data: T) => T | Promise<T>
10+
): Promise<OpenApiDocument> {
711
let currentObject = document as unknown as JsonValue;
812
for (const key of path.slice(0, -1)) {
913
currentObject = (currentObject as JsonObject)[key];
@@ -16,57 +20,52 @@ function patch<T>(document: OpenApiDocument, path: string[], patch: (data: T) =>
1620
}
1721
}
1822
const lastPathItem = path[path.length - 1];
19-
(currentObject as JsonObject)[lastPathItem] = patch((currentObject as JsonObject)[lastPathItem] as T) as JsonValue;
23+
(currentObject as JsonObject)[lastPathItem] = (await patch(
24+
(currentObject as JsonObject)[lastPathItem] as T
25+
)) as JsonValue;
2026
return document;
2127
}
2228

23-
export function patchOpenApiDocument(
29+
export async function patchOpenApiDocument(
2430
document: OpenApiDocument,
2531
config: CommonOpenApiClientGeneratorConfigDocumentPatch
26-
): OpenApiDocument {
32+
): Promise<OpenApiDocument> {
2733
let result = document;
2834
if (config.patchPaths) {
2935
if (typeof config.patchPaths === 'function') {
30-
result = patch(result, ['paths'], config.patchPaths);
36+
result = await patch(result, ['paths'], config.patchPaths);
3137
} else {
3238
for (const [path, pathItemPatch] of Object.entries(config.patchPaths)) {
3339
if (typeof pathItemPatch === 'function') {
34-
result = patch(
35-
result,
36-
['paths', path],
37-
(pathItem: OpenApiPathItem): OpenApiPathItem => pathItemPatch(pathItem, path)
40+
result = await patch(result, ['paths', path], (pathItem: OpenApiPathItem) =>
41+
pathItemPatch(pathItem, path)
3842
);
3943
} else {
4044
for (const [method, methodPatch] of Object.entries(pathItemPatch)) {
41-
result = patch(
42-
result,
43-
['paths', path, method],
44-
(operation: OpenApiOperation): OpenApiOperation =>
45-
methodPatch(operation, path, method as OpenApiHttpMethod)
45+
result = await patch(result, ['paths', path, method], (operation: OpenApiOperation) =>
46+
methodPatch(operation, path, method as OpenApiHttpMethod)
4647
);
4748
}
4849
}
4950
}
5051
}
5152
}
5253
if (config.patchTags) {
53-
result = patch(result, ['tags'], config.patchTags);
54+
result = await patch(result, ['tags'], config.patchTags);
5455
}
5556
if (config.patchSchemas) {
5657
if (typeof config.patchSchemas === 'function') {
57-
result = patch(result, ['components', 'schemas'], config.patchSchemas);
58+
result = await patch(result, ['components', 'schemas'], config.patchSchemas);
5859
} else {
5960
for (const [schemaName, schemaPatch] of Object.entries(config.patchSchemas)) {
60-
result = patch(
61-
result,
62-
['components', 'schemas', schemaName],
63-
(schema: OpenApiSchema): OpenApiSchema => schemaPatch(schema, schemaName)
61+
result = await patch(result, ['components', 'schemas', schemaName], (schema: OpenApiSchema) =>
62+
schemaPatch(schema, schemaName)
6463
);
6564
}
6665
}
6766
}
6867
if (config.patchDocument) {
69-
result = config.patchDocument(result);
68+
result = await config.patchDocument(result);
7069
}
7170
return result;
7271
}
Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
import path from 'path';
22
import {ApiTypescriptGeneratorConfig} from './../../src';
33

4-
const configuration: ApiTypescriptGeneratorConfig = {
5-
generates: [
6-
{
7-
type: 'openapiClient',
8-
document: {
9-
source: {
10-
type: 'url',
11-
url: 'https://raw.githubusercontent.com/readmeio/oas-examples/main/3.1/yaml/petstore.yaml'
4+
export default async function (): Promise<ApiTypescriptGeneratorConfig> {
5+
return {
6+
generates: [
7+
{
8+
type: 'openapiClient',
9+
document: {
10+
source: {
11+
type: 'url',
12+
url: 'https://raw.githubusercontent.com/readmeio/oas-examples/main/3.1/yaml/petstore.yaml'
13+
}
14+
},
15+
outputDirPath: path.join(__dirname, 'petstore-api-client'),
16+
client: {
17+
name: 'PetStoreApiClient'
18+
},
19+
operations: {
20+
validateResponse: true,
21+
makeResponseValidationSchemasExtensible: true
22+
},
23+
validation: {
24+
library: 'zod'
25+
},
26+
postprocess: {
27+
eslint: true
1228
}
13-
},
14-
outputDirPath: path.join(__dirname, 'petstore-api-client'),
15-
client: {
16-
name: 'PetStoreApiClient'
17-
},
18-
operations: {
19-
validateResponse: true,
20-
makeResponseValidationSchemasExtensible: true
21-
},
22-
validation: {
23-
library: 'zod'
24-
},
25-
postprocess: {
26-
eslint: true
2729
}
28-
}
29-
]
30-
};
31-
32-
export default configuration;
30+
]
31+
};
32+
}

0 commit comments

Comments
 (0)