@goast/core 0.4.1-springwebclient1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +67 -67
- package/package.json +1 -1
- package/src/_dnt.shims.ts +69 -0
- package/src/mod.ts +33 -0
- package/src/src/ast/node.ts +46 -0
- package/src/src/codegen/config.ts +28 -0
- package/src/src/codegen/endpoints-generator.ts +72 -0
- package/src/src/codegen/generator.ts +181 -0
- package/src/src/codegen/internal-utils.ts +6 -0
- package/src/src/codegen/schemas-generator.ts +87 -0
- package/src/src/codegen/services-generator.ts +77 -0
- package/src/src/codegen/types.ts +37 -0
- package/src/src/collect/collector.ts +142 -0
- package/src/src/collect/helpers.ts +36 -0
- package/src/src/collect/types.ts +21 -0
- package/src/src/internal-utils.ts +5 -0
- package/src/src/parse/deref-proxy.ts +64 -0
- package/src/src/parse/openapi-types.ts +949 -0
- package/src/src/parse/parser.ts +220 -0
- package/src/src/parse/types.ts +27 -0
- package/src/src/transform/api-types.ts +243 -0
- package/src/src/transform/helpers.ts +216 -0
- package/src/src/transform/transform-document.ts +35 -0
- package/src/src/transform/transform-endpoint.ts +331 -0
- package/src/src/transform/transform-schema.ts +217 -0
- package/src/src/transform/transformer.ts +52 -0
- package/src/src/transform/types.ts +52 -0
- package/src/src/utils/action-provider.ts +58 -0
- package/src/src/utils/array.utils.ts +7 -0
- package/src/src/utils/asset-manager.ts +51 -0
- package/src/src/utils/class.utils.ts +40 -0
- package/src/src/utils/common.utils.ts +86 -0
- package/src/src/utils/condition.ts +7 -0
- package/src/src/utils/endpoint.utils.ts +10 -0
- package/src/src/utils/error.utils.ts +3 -0
- package/src/src/utils/factory.ts +42 -0
- package/src/src/utils/file-system.utils.ts +48 -0
- package/src/src/utils/generator.utils.ts +6 -0
- package/src/src/utils/object.utils.ts +32 -0
- package/src/src/utils/schema.utils.ts +142 -0
- package/src/src/utils/source-builder.ts +597 -0
- package/src/src/utils/string-builder/options.ts +19 -0
- package/src/src/utils/string-builder/string-builder.ts +165 -0
- package/src/src/utils/string-builder/utils.ts +69 -0
- package/src/src/utils/string.utils.ts +369 -0
- package/src/src/utils/string.utils.types.ts +179 -0
- package/src/src/utils/type.utils.ts +93 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { parse as parsePath } from 'node:path';
|
|
2
|
+
import { isOpenApiObjectProperty } from '../internal-utils.js';
|
|
3
|
+
import type { OpenApiHttpMethod, OpenApiObject } from '../parse/openapi-types.js';
|
|
4
|
+
import type { Deref, DerefSource } from '../parse/types.js';
|
|
5
|
+
import { isNullish } from '../utils/common.utils.js';
|
|
6
|
+
import { getDeepProperty } from '../utils/object.utils.js';
|
|
7
|
+
import type { ApiSchema, ApiSchemaAccessibility, ApiSchemaKind, ApiSchemaProperty } from './api-types.js';
|
|
8
|
+
import type { OpenApiTransformerContext } from './types.js';
|
|
9
|
+
|
|
10
|
+
export function determineSchemaKind<
|
|
11
|
+
T extends {
|
|
12
|
+
oneOf?: unknown;
|
|
13
|
+
allOf?: unknown;
|
|
14
|
+
anyOf?: unknown;
|
|
15
|
+
type?: string | string[];
|
|
16
|
+
properties?: Record<string, unknown>;
|
|
17
|
+
additionalProperties?: unknown;
|
|
18
|
+
},
|
|
19
|
+
>(ctx: OpenApiTransformerContext, schema: T): ApiSchemaKind {
|
|
20
|
+
if (schema.oneOf) {
|
|
21
|
+
return 'oneOf';
|
|
22
|
+
} else if (schema.type !== 'object' && (schema.allOf || schema.anyOf)) {
|
|
23
|
+
const hasProperties = (schema.properties && Object.keys(schema.properties).length > 0) ||
|
|
24
|
+
schema.additionalProperties;
|
|
25
|
+
return hasProperties ? 'object' : 'combined';
|
|
26
|
+
} else if (Array.isArray(schema.type)) {
|
|
27
|
+
return 'multi-type';
|
|
28
|
+
} else if (
|
|
29
|
+
schema.type === 'object' ||
|
|
30
|
+
schema.type === 'string' ||
|
|
31
|
+
schema.type === 'boolean' ||
|
|
32
|
+
schema.type === 'null' ||
|
|
33
|
+
schema.type === 'number' ||
|
|
34
|
+
schema.type === 'integer' ||
|
|
35
|
+
schema.type === 'array'
|
|
36
|
+
) {
|
|
37
|
+
return schema.type;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const treadAsObject = ctx.config.unknownTypeBehavior === 'always-object' ||
|
|
41
|
+
(ctx.config.unknownTypeBehavior === 'object-if-properties' &&
|
|
42
|
+
schema.properties &&
|
|
43
|
+
Object.keys(schema.properties).length > 0);
|
|
44
|
+
|
|
45
|
+
return treadAsObject ? 'object' : 'unknown';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function determineSchemaName(
|
|
49
|
+
schema: {
|
|
50
|
+
title?: string;
|
|
51
|
+
$src: DerefSource<unknown>;
|
|
52
|
+
},
|
|
53
|
+
id: string,
|
|
54
|
+
): { name: string; isGenerated: boolean } {
|
|
55
|
+
if (schema.title) return { name: schema.title, isGenerated: false };
|
|
56
|
+
if (!schema.$src) {
|
|
57
|
+
console.log('woot?');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (schema.$src.path === '/') return { name: parsePath(schema.$src.file).name, isGenerated: true };
|
|
61
|
+
|
|
62
|
+
const schemaNameMatch = schema.$src.path.match(/(?<=\/components\/schemas\/|\/definitions\/)[^/]+$/i);
|
|
63
|
+
if (schemaNameMatch) {
|
|
64
|
+
return { name: schemaNameMatch[0], isGenerated: false };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const paramNameMatch = schema.$src.path.match(/\/components\/parameters\/([^/]+)\/schema$/i);
|
|
68
|
+
if (paramNameMatch) {
|
|
69
|
+
return { name: paramNameMatch[1], isGenerated: true };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const responseMatch = schema.$src.path.match(/\/paths\/(?<path>.+)\/(?<method>.+)\/responses\/(?<status>\d+)\//);
|
|
73
|
+
if (responseMatch && responseMatch.groups) {
|
|
74
|
+
const { path, method, status } = responseMatch.groups;
|
|
75
|
+
const operation = schema.$src.document.paths?.[path]?.[method as OpenApiHttpMethod] ?? {};
|
|
76
|
+
return {
|
|
77
|
+
name: `${determineEndpointName({ method, path: `/${path}`, operation })}_${status}_Response`,
|
|
78
|
+
isGenerated: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const responseCompMatch = schema.$src.path.match(/\/components\/responses\/([^/]+)\/content\/.+\/schema/);
|
|
83
|
+
if (responseCompMatch) {
|
|
84
|
+
return {
|
|
85
|
+
name: responseCompMatch[1].toLowerCase().endsWith('response')
|
|
86
|
+
? responseCompMatch[1]
|
|
87
|
+
: responseCompMatch[1] + 'Response',
|
|
88
|
+
isGenerated: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const requestBodyMatch = schema.$src.path.match(/\/paths\/(?<path>.+)\/(?<method>.+)\/requestBody\//);
|
|
93
|
+
if (requestBodyMatch && requestBodyMatch.groups) {
|
|
94
|
+
const { path, method } = requestBodyMatch.groups;
|
|
95
|
+
const operation = schema.$src.document.paths?.[path]?.[method as OpenApiHttpMethod] ?? {};
|
|
96
|
+
return { name: `${determineEndpointName({ method, path: `/${path}`, operation })}_Request`, isGenerated: true };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const parentSchemaMatch = schema.$src.path.match(/(.*)\/properties\/([^/]*)(\/additionalProperties)?$/);
|
|
100
|
+
if (parentSchemaMatch) {
|
|
101
|
+
const parentSchema = getDeepProperty(schema.$src.document, parentSchemaMatch[1].split('/').filter(Boolean));
|
|
102
|
+
const parentSchemaName = determineSchemaName(
|
|
103
|
+
{
|
|
104
|
+
title: typeof parentSchema === 'object' && parentSchema !== null && 'title' in parentSchema
|
|
105
|
+
? String(parentSchema.title)
|
|
106
|
+
: undefined,
|
|
107
|
+
$src: {
|
|
108
|
+
file: schema.$src.file,
|
|
109
|
+
path: parentSchemaMatch[1],
|
|
110
|
+
document: schema.$src.document,
|
|
111
|
+
originalComponent: parentSchema,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
id,
|
|
115
|
+
);
|
|
116
|
+
if (!parentSchemaName.isGenerated) {
|
|
117
|
+
return { name: `${parentSchemaName.name}_${parentSchemaMatch[2]}`, isGenerated: true };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return { name: id, isGenerated: true };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function determineSchemaAccessibility(schema: {
|
|
125
|
+
readOnly?: boolean;
|
|
126
|
+
writeOnly?: boolean;
|
|
127
|
+
}): ApiSchemaAccessibility {
|
|
128
|
+
if (schema.readOnly === true) {
|
|
129
|
+
return schema.writeOnly === true ? 'none' : 'readOnly';
|
|
130
|
+
} else {
|
|
131
|
+
return schema.writeOnly === true ? 'writeOnly' : 'all';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function updateSchemaAccessibility(
|
|
136
|
+
accessibility: ApiSchemaAccessibility,
|
|
137
|
+
schema: {
|
|
138
|
+
readOnly?: boolean;
|
|
139
|
+
writeOnly?: boolean;
|
|
140
|
+
},
|
|
141
|
+
): ApiSchemaAccessibility {
|
|
142
|
+
if (accessibility === 'none') return 'none';
|
|
143
|
+
if (accessibility === 'readOnly') return schema.writeOnly === true ? 'none' : 'readOnly';
|
|
144
|
+
if (accessibility === 'writeOnly') return schema.readOnly === true ? 'none' : 'writeOnly';
|
|
145
|
+
return determineSchemaAccessibility(schema);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
type CustomFields<T extends Record<string, unknown>> = {
|
|
149
|
+
[K in keyof T as K extends `x-${string}` ? K : never]: T[K];
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export function getCustomFields<T extends Record<string, unknown>>(schema: T): CustomFields<T> {
|
|
153
|
+
const result = {} as CustomFields<T>;
|
|
154
|
+
for (const key in schema) {
|
|
155
|
+
if (key.startsWith('x-')) {
|
|
156
|
+
const name = key.substring(2);
|
|
157
|
+
(result as Record<string, unknown>)[name] = schema[key];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function determineEndpointName(endpointInfo: {
|
|
164
|
+
method: string;
|
|
165
|
+
path: string;
|
|
166
|
+
operation: { operationId?: string };
|
|
167
|
+
}): string {
|
|
168
|
+
if (endpointInfo.operation.operationId) return endpointInfo.operation.operationId;
|
|
169
|
+
return endpointInfo.method + endpointInfo.path.replace(/\{([^}]+)\}/g, ':$1').replace(/\//g, '_');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function transformAdditionalProperties<TAdditionalProperties>(
|
|
173
|
+
context: OpenApiTransformerContext,
|
|
174
|
+
schema: {
|
|
175
|
+
additionalProperties?: TAdditionalProperties;
|
|
176
|
+
},
|
|
177
|
+
transformSchema: (
|
|
178
|
+
context: OpenApiTransformerContext,
|
|
179
|
+
schema: Exclude<TAdditionalProperties, undefined | boolean>,
|
|
180
|
+
) => ApiSchema,
|
|
181
|
+
): boolean | ApiSchema | undefined {
|
|
182
|
+
if (isNullish(schema.additionalProperties)) return undefined;
|
|
183
|
+
if (typeof schema.additionalProperties === 'boolean') return schema.additionalProperties;
|
|
184
|
+
return transformSchema(context, schema.additionalProperties as Exclude<TAdditionalProperties, undefined | boolean>);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function transformSchemaProperties<TProperties>(
|
|
188
|
+
context: OpenApiTransformerContext,
|
|
189
|
+
schema: { properties?: Record<string, TProperties>; required?: string[] },
|
|
190
|
+
transformSchema: (context: OpenApiTransformerContext, schema: TProperties) => ApiSchema,
|
|
191
|
+
): Map<string, ApiSchemaProperty> {
|
|
192
|
+
const result = new Map<string, ApiSchemaProperty>();
|
|
193
|
+
if (!schema.properties) return result;
|
|
194
|
+
for (const name of Object.keys(schema.properties)) {
|
|
195
|
+
if (!isOpenApiObjectProperty(name)) continue;
|
|
196
|
+
result.set(name, {
|
|
197
|
+
name,
|
|
198
|
+
schema: transformSchema(context, schema.properties[name]),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export class IdGenerator {
|
|
205
|
+
private readonly _idMap = new Map<string, number>();
|
|
206
|
+
|
|
207
|
+
public generateId(name: string): string {
|
|
208
|
+
const id = this._idMap.get(name) ?? 1;
|
|
209
|
+
this._idMap.set(name, id + 1);
|
|
210
|
+
return `${name}-${id}`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function getOpenApiObjectIdentifier(obj: Deref<OpenApiObject<string>>) {
|
|
215
|
+
return obj.$src.file + '#' + obj.$src.path;
|
|
216
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { OpenApiDocument, OpenApiTag } from '../parse/openapi-types.js';
|
|
2
|
+
import type { Deref } from '../parse/types.js';
|
|
3
|
+
import type { ApiService } from './api-types.js';
|
|
4
|
+
import { getOpenApiObjectIdentifier } from './helpers.js';
|
|
5
|
+
import type { OpenApiTransformerContext } from './types.js';
|
|
6
|
+
|
|
7
|
+
export function transformDocument(context: OpenApiTransformerContext, document: Deref<OpenApiDocument>) {
|
|
8
|
+
for (const tag of document.tags ?? []) {
|
|
9
|
+
transformTag(context, tag);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function transformTag(context: OpenApiTransformerContext, tag: Deref<OpenApiTag>, isReference = false) {
|
|
14
|
+
const openApiObjectId = getOpenApiObjectIdentifier(tag);
|
|
15
|
+
const existing = context.transformed.services.get(openApiObjectId);
|
|
16
|
+
if (existing) return existing;
|
|
17
|
+
|
|
18
|
+
const ref = tag.$ref ? transformTag(context, tag.$ref, true) : undefined;
|
|
19
|
+
const id = context.idGenerator.generateId('service');
|
|
20
|
+
const service: ApiService = {
|
|
21
|
+
$src: {
|
|
22
|
+
...tag.$src,
|
|
23
|
+
component: tag,
|
|
24
|
+
},
|
|
25
|
+
$ref: ref,
|
|
26
|
+
id,
|
|
27
|
+
name: tag.name ?? id,
|
|
28
|
+
description: tag.description,
|
|
29
|
+
endpoints: [],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
context.transformed.services.set(openApiObjectId, service);
|
|
33
|
+
if (!isReference) context.services.set(service.name, service);
|
|
34
|
+
return service;
|
|
35
|
+
}
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import type { OpenApiCollectorEndpointInfo } from '../collect/types.js';
|
|
2
|
+
import { isOpenApiObjectProperty } from '../internal-utils.js';
|
|
3
|
+
import type {
|
|
4
|
+
OpenApiHeader,
|
|
5
|
+
OpenApiMediaType,
|
|
6
|
+
OpenApiParameter,
|
|
7
|
+
OpenApiPathItem,
|
|
8
|
+
OpenApiRequestBody,
|
|
9
|
+
OpenApiResponse,
|
|
10
|
+
OpenApiSchema,
|
|
11
|
+
} from '../parse/openapi-types.js';
|
|
12
|
+
import type { Deref } from '../parse/types.js';
|
|
13
|
+
import type {
|
|
14
|
+
ApiContent,
|
|
15
|
+
ApiEndpoint,
|
|
16
|
+
ApiHeader,
|
|
17
|
+
ApiParameter,
|
|
18
|
+
ApiParameterTarget,
|
|
19
|
+
ApiPath,
|
|
20
|
+
ApiRequestBody,
|
|
21
|
+
ApiResponse,
|
|
22
|
+
} from './api-types.js';
|
|
23
|
+
import { determineEndpointName, getCustomFields, getOpenApiObjectIdentifier } from './helpers.js';
|
|
24
|
+
import { transformSchema } from './transform-schema.js';
|
|
25
|
+
import type { OpenApiTransformerContext } from './types.js';
|
|
26
|
+
|
|
27
|
+
export function transformEndpoint(context: OpenApiTransformerContext, endpointInfo: OpenApiCollectorEndpointInfo) {
|
|
28
|
+
const apiPath = transformApiPath(context, endpointInfo.path, endpointInfo.pathItem);
|
|
29
|
+
const endpoint: ApiEndpoint = {
|
|
30
|
+
$src: {
|
|
31
|
+
...endpointInfo.operation.$src,
|
|
32
|
+
component: endpointInfo.operation,
|
|
33
|
+
},
|
|
34
|
+
$ref: undefined,
|
|
35
|
+
id: context.idGenerator.generateId('endpoint'),
|
|
36
|
+
name: determineEndpointName(endpointInfo),
|
|
37
|
+
path: endpointInfo.path,
|
|
38
|
+
pathInfo: apiPath,
|
|
39
|
+
method: endpointInfo.method,
|
|
40
|
+
summary: endpointInfo.operation.summary,
|
|
41
|
+
description: endpointInfo.operation.description,
|
|
42
|
+
parameters: combineParameters(
|
|
43
|
+
apiPath.parameters,
|
|
44
|
+
endpointInfo.operation.parameters?.map((p) => transformParameter(context, p)) ?? [],
|
|
45
|
+
),
|
|
46
|
+
deprecated: endpointInfo.operation.deprecated ?? false,
|
|
47
|
+
requestBody: endpointInfo.operation.requestBody
|
|
48
|
+
? transformRequestBody(context, endpointInfo.operation.requestBody)
|
|
49
|
+
: undefined,
|
|
50
|
+
responses: transformResponses(context, endpointInfo.operation.responses),
|
|
51
|
+
tags: endpointInfo.operation.tags ?? [],
|
|
52
|
+
custom: getCustomFields(endpointInfo.operation),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const tags = endpoint.tags.length === 0 ? [''] : endpoint.tags;
|
|
56
|
+
for (const tag of tags) {
|
|
57
|
+
let service = context.services.get(tag);
|
|
58
|
+
if (!service) {
|
|
59
|
+
const id = context.idGenerator.generateId('service');
|
|
60
|
+
service = {
|
|
61
|
+
$ref: undefined,
|
|
62
|
+
id,
|
|
63
|
+
name: tag || id,
|
|
64
|
+
description: undefined,
|
|
65
|
+
endpoints: [],
|
|
66
|
+
};
|
|
67
|
+
context.services.set(tag, service);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
service.endpoints.push(endpoint);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
apiPath[endpointInfo.method] = endpoint;
|
|
74
|
+
context.endpoints.set(`${endpointInfo.method}:${endpointInfo.path}`, endpoint);
|
|
75
|
+
return endpoint;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function transformApiPath(
|
|
79
|
+
context: OpenApiTransformerContext,
|
|
80
|
+
path: string,
|
|
81
|
+
pathItem: Deref<OpenApiPathItem>,
|
|
82
|
+
isReference = false,
|
|
83
|
+
): ApiPath {
|
|
84
|
+
const existingPath = context.paths.get(path);
|
|
85
|
+
if (existingPath) return existingPath;
|
|
86
|
+
|
|
87
|
+
const openApiObjectId = getOpenApiObjectIdentifier(pathItem);
|
|
88
|
+
const existing = context.transformed.paths.get(openApiObjectId);
|
|
89
|
+
if (existing) return existing;
|
|
90
|
+
|
|
91
|
+
const ref = pathItem.$ref ? transformApiPath(context, path, pathItem.$ref, true) : undefined;
|
|
92
|
+
const apiPath: ApiPath = {
|
|
93
|
+
$src: {
|
|
94
|
+
...pathItem.$src,
|
|
95
|
+
component: pathItem,
|
|
96
|
+
},
|
|
97
|
+
$ref: ref,
|
|
98
|
+
id: context.idGenerator.generateId('path'),
|
|
99
|
+
summary: pathItem.summary,
|
|
100
|
+
description: pathItem.description,
|
|
101
|
+
path: path,
|
|
102
|
+
parameters: pathItem.parameters?.map((p) => transformParameter(context, p)) ?? [],
|
|
103
|
+
|
|
104
|
+
// HTTP methods are added later (see transformEndpoint)
|
|
105
|
+
get: undefined,
|
|
106
|
+
put: undefined,
|
|
107
|
+
post: undefined,
|
|
108
|
+
delete: undefined,
|
|
109
|
+
options: undefined,
|
|
110
|
+
head: undefined,
|
|
111
|
+
patch: undefined,
|
|
112
|
+
trace: undefined,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
context.transformed.paths.set(openApiObjectId, apiPath);
|
|
116
|
+
if (!isReference) context.paths.set(path, apiPath);
|
|
117
|
+
return apiPath;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function transformParameter(context: OpenApiTransformerContext, parameter: Deref<OpenApiParameter>): ApiParameter {
|
|
121
|
+
const openApiObjectId = getOpenApiObjectIdentifier(parameter);
|
|
122
|
+
const existing = context.transformed.parameters.get(openApiObjectId);
|
|
123
|
+
if (existing) return existing;
|
|
124
|
+
|
|
125
|
+
const ref = parameter.$ref ? transformParameter(context, parameter.$ref) : undefined;
|
|
126
|
+
const id = context.idGenerator.generateId('parameter');
|
|
127
|
+
const p: ApiParameter = {
|
|
128
|
+
$src: {
|
|
129
|
+
...parameter.$src,
|
|
130
|
+
component: parameter,
|
|
131
|
+
},
|
|
132
|
+
$ref: ref,
|
|
133
|
+
id: id,
|
|
134
|
+
name: parameter.name ?? id,
|
|
135
|
+
target: parameter.in as ApiParameterTarget,
|
|
136
|
+
description: parameter.description,
|
|
137
|
+
required: parameter.required ?? false,
|
|
138
|
+
deprecated: parameter.deprecated ?? false,
|
|
139
|
+
allowEmptyValue: parameter.allowEmptyValue,
|
|
140
|
+
style: parameter.style,
|
|
141
|
+
explode: parameter.explode,
|
|
142
|
+
allowReserved: parameter.allowReserved,
|
|
143
|
+
schema: parameter.schema ? transformSchema(context, parameter.schema) : undefined,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
context.transformed.parameters.set(openApiObjectId, p);
|
|
147
|
+
return p;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function transformRequestBody(
|
|
151
|
+
context: OpenApiTransformerContext,
|
|
152
|
+
requestBody: Deref<OpenApiRequestBody>,
|
|
153
|
+
): ApiRequestBody {
|
|
154
|
+
const openApiObjectId = getOpenApiObjectIdentifier(requestBody);
|
|
155
|
+
const existing = context.transformed.requestBodies.get(openApiObjectId);
|
|
156
|
+
if (existing) return existing;
|
|
157
|
+
|
|
158
|
+
const ref = requestBody.$ref ? transformRequestBody(context, requestBody.$ref) : undefined;
|
|
159
|
+
const rb: ApiRequestBody = {
|
|
160
|
+
$src: {
|
|
161
|
+
...requestBody.$src,
|
|
162
|
+
component: requestBody,
|
|
163
|
+
},
|
|
164
|
+
$ref: ref,
|
|
165
|
+
id: context.idGenerator.generateId('requestBody'),
|
|
166
|
+
description: requestBody.description,
|
|
167
|
+
required: requestBody.required ?? false,
|
|
168
|
+
content: transformContent(context, requestBody.content),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
context.transformed.requestBodies.set(openApiObjectId, rb);
|
|
172
|
+
return rb;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function transformResponses(
|
|
176
|
+
context: OpenApiTransformerContext,
|
|
177
|
+
responses?: Record<string, Deref<OpenApiResponse>>,
|
|
178
|
+
): ApiResponse[] {
|
|
179
|
+
if (!responses) return [];
|
|
180
|
+
const result: ApiResponse[] = [];
|
|
181
|
+
for (const status of Object.keys(responses)) {
|
|
182
|
+
if (!isOpenApiObjectProperty(status)) continue;
|
|
183
|
+
result.push(transformResponse(context, responses[status], status));
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function transformResponse(
|
|
189
|
+
context: OpenApiTransformerContext,
|
|
190
|
+
response: Deref<OpenApiResponse>,
|
|
191
|
+
status: string,
|
|
192
|
+
): ApiResponse {
|
|
193
|
+
const openApiObjectId = getOpenApiObjectIdentifier(response);
|
|
194
|
+
const existing = context.transformed.responses.get(openApiObjectId);
|
|
195
|
+
if (existing) return existing;
|
|
196
|
+
|
|
197
|
+
const ref = response.$ref ? transformResponse(context, response.$ref, status) : undefined;
|
|
198
|
+
const r: ApiResponse = {
|
|
199
|
+
$src: {
|
|
200
|
+
...response.$src,
|
|
201
|
+
component: response,
|
|
202
|
+
},
|
|
203
|
+
$ref: ref,
|
|
204
|
+
id: context.idGenerator.generateId('response'),
|
|
205
|
+
statusCode: Number(status) || undefined,
|
|
206
|
+
description: response.description,
|
|
207
|
+
headers: transformHeaders(context, response.headers),
|
|
208
|
+
contentOptions: transformContent(context, response.content),
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
context.transformed.responses.set(openApiObjectId, r);
|
|
212
|
+
return r;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function transformContent(
|
|
216
|
+
context: OpenApiTransformerContext,
|
|
217
|
+
content?: Record<string, Deref<OpenApiMediaType>>,
|
|
218
|
+
): ApiContent[] {
|
|
219
|
+
if (!content) return [];
|
|
220
|
+
const result: ApiContent[] = [];
|
|
221
|
+
for (const media of Object.keys(content)) {
|
|
222
|
+
if (!isOpenApiObjectProperty(media)) continue;
|
|
223
|
+
result.push(transformMediaType(context, content[media], media));
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function transformMediaType(
|
|
229
|
+
context: OpenApiTransformerContext,
|
|
230
|
+
mediaType: Deref<OpenApiMediaType>,
|
|
231
|
+
media: string,
|
|
232
|
+
): ApiContent {
|
|
233
|
+
const openApiObjectId = getOpenApiObjectIdentifier(mediaType);
|
|
234
|
+
const existing = context.transformed.content.get(openApiObjectId);
|
|
235
|
+
if (existing) return existing;
|
|
236
|
+
|
|
237
|
+
const ref = mediaType.$ref ? transformMediaType(context, mediaType.$ref, media) : undefined;
|
|
238
|
+
const mt: ApiContent = {
|
|
239
|
+
$src: {
|
|
240
|
+
...mediaType.$src,
|
|
241
|
+
component: mediaType,
|
|
242
|
+
},
|
|
243
|
+
$ref: ref,
|
|
244
|
+
id: context.idGenerator.generateId('content'),
|
|
245
|
+
type: media,
|
|
246
|
+
schema: mediaType.schema ? transformSchema(context, mediaType.schema) : undefined,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
context.transformed.content.set(openApiObjectId, mt);
|
|
250
|
+
return mt;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function combineParameters(pathParams: ApiParameter[], operationParams: ApiParameter[]): ApiParameter[] {
|
|
254
|
+
const result = [...pathParams];
|
|
255
|
+
for (const opParam of operationParams) {
|
|
256
|
+
const existingParam = result.findIndex((p) => p.name === opParam.name);
|
|
257
|
+
if (existingParam >= 0) {
|
|
258
|
+
result[existingParam] = opParam;
|
|
259
|
+
} else {
|
|
260
|
+
result.push(opParam);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function isSchema(obj: Record<string, unknown>): obj is Deref<OpenApiSchema> {
|
|
267
|
+
return obj && typeof obj === 'object' && obj['type'] !== undefined;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function transformHeaders(
|
|
271
|
+
context: OpenApiTransformerContext,
|
|
272
|
+
headers?: Record<string, Deref<OpenApiHeader> | Deref<OpenApiSchema>>,
|
|
273
|
+
): ApiHeader[] {
|
|
274
|
+
if (!headers) return [];
|
|
275
|
+
const result: ApiHeader[] = [];
|
|
276
|
+
for (const name of Object.keys(headers)) {
|
|
277
|
+
if (!isOpenApiObjectProperty(name)) continue;
|
|
278
|
+
result.push(transformHeader(context, headers[name], name));
|
|
279
|
+
}
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function transformHeader(
|
|
284
|
+
context: OpenApiTransformerContext,
|
|
285
|
+
header: Deref<OpenApiHeader> | Deref<OpenApiSchema>,
|
|
286
|
+
name: string,
|
|
287
|
+
): ApiHeader {
|
|
288
|
+
const openApiObjectId = getOpenApiObjectIdentifier(header);
|
|
289
|
+
const existing = context.transformed.headers.get(openApiObjectId);
|
|
290
|
+
if (existing) return existing;
|
|
291
|
+
|
|
292
|
+
let h: ApiHeader;
|
|
293
|
+
const ref = header.$ref ? transformHeader(context, header.$ref, name) : undefined;
|
|
294
|
+
const id = context.idGenerator.generateId('header');
|
|
295
|
+
if (isSchema(header)) {
|
|
296
|
+
h = {
|
|
297
|
+
// deno-lint-ignore no-explicit-any
|
|
298
|
+
$src: Object.assign({}, header.$src, { component: header }) as any,
|
|
299
|
+
$ref: ref as ApiHeader['$ref'],
|
|
300
|
+
id,
|
|
301
|
+
name,
|
|
302
|
+
description: header.description,
|
|
303
|
+
required: false,
|
|
304
|
+
deprecated: false,
|
|
305
|
+
allowEmptyValue: undefined,
|
|
306
|
+
style: undefined,
|
|
307
|
+
explode: undefined,
|
|
308
|
+
allowReserved: undefined,
|
|
309
|
+
schema: transformSchema(context, header),
|
|
310
|
+
};
|
|
311
|
+
} else {
|
|
312
|
+
h = {
|
|
313
|
+
// deno-lint-ignore no-explicit-any
|
|
314
|
+
$src: Object.assign({}, header.$src, { component: header }) as any,
|
|
315
|
+
$ref: ref as ApiHeader['$ref'],
|
|
316
|
+
id,
|
|
317
|
+
name,
|
|
318
|
+
description: header.description,
|
|
319
|
+
required: header.required ?? false,
|
|
320
|
+
deprecated: header.deprecated ?? false,
|
|
321
|
+
allowEmptyValue: header.allowEmptyValue,
|
|
322
|
+
style: header.style,
|
|
323
|
+
explode: header.explode,
|
|
324
|
+
allowReserved: header.allowReserved,
|
|
325
|
+
schema: header.schema ? transformSchema(context, header.schema) : undefined,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
context.transformed.headers.set(openApiObjectId, h);
|
|
330
|
+
return h;
|
|
331
|
+
}
|