@openapi-qraft/plugin 1.13.0 → 1.14.0-beta.0
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/CHANGELOG.md +7 -0
- package/dist/lib/QraftCommand.d.ts.map +1 -1
- package/dist/lib/QraftCommand.js +27 -8
- package/dist/lib/QraftCommand.js.map +1 -1
- package/dist/lib/createServicePathMatch.d.ts +0 -9
- package/dist/lib/createServicePathMatch.d.ts.map +1 -1
- package/dist/lib/createServicePathMatch.js +0 -15
- package/dist/lib/createServicePathMatch.js.map +1 -1
- package/dist/lib/filterDocumentPaths.d.ts +1 -1
- package/dist/lib/filterDocumentPaths.d.ts.map +1 -1
- package/dist/lib/filterDocumentPaths.js +1 -1
- package/dist/lib/filterDocumentPaths.js.map +1 -1
- package/dist/lib/parseOperationGlobs.d.ts +17 -0
- package/dist/lib/parseOperationGlobs.d.ts.map +1 -0
- package/dist/lib/parseOperationGlobs.js +37 -0
- package/dist/lib/parseOperationGlobs.js.map +1 -0
- package/dist/lib/predefineSchemaParameters.d.ts +15 -11
- package/dist/lib/predefineSchemaParameters.d.ts.map +1 -1
- package/dist/lib/predefineSchemaParameters.js +50 -30
- package/dist/lib/predefineSchemaParameters.js.map +1 -1
- package/dist/lib/processOperationNameModifierOption.d.ts +57 -0
- package/dist/lib/processOperationNameModifierOption.d.ts.map +1 -0
- package/dist/lib/processOperationNameModifierOption.js +102 -0
- package/dist/lib/processOperationNameModifierOption.js.map +1 -0
- package/dist/lib/splitCommaSeparatedGlobs.d.ts +7 -0
- package/dist/lib/splitCommaSeparatedGlobs.d.ts.map +1 -0
- package/dist/lib/splitCommaSeparatedGlobs.js +16 -0
- package/dist/lib/splitCommaSeparatedGlobs.js.map +1 -0
- package/package.json +3 -3
- package/src/lib/QraftCommand.ts +59 -10
- package/src/lib/createServicePathMatch.ts +0 -18
- package/src/lib/filterDocumentPaths.ts +2 -2
- package/src/lib/parseOperationGlobs.ts +54 -0
- package/src/lib/predefineSchemaParameters.ts +108 -71
- package/src/lib/processOperationNameModifierOption.ts +205 -0
- package/src/lib/splitCommaSeparatedGlobs.ts +16 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createServicePathMatch } from './createServicePathMatch.js';
|
|
2
|
+
import { parseOperationGlobs, } from './parseOperationGlobs.js';
|
|
3
|
+
import { splitCommaSeparatedGlobs } from './splitCommaSeparatedGlobs.js';
|
|
4
|
+
export const processOperationNameModifierOption = (modifiers, services) => {
|
|
5
|
+
if (!modifiers.length)
|
|
6
|
+
return {
|
|
7
|
+
errors: [],
|
|
8
|
+
services,
|
|
9
|
+
};
|
|
10
|
+
const operationNameModifiers = modifiers.map((modifier) => ({
|
|
11
|
+
...modifier,
|
|
12
|
+
isPathMatch: createServicePathMatch(splitCommaSeparatedGlobs(modifier.pathGlobs)),
|
|
13
|
+
}));
|
|
14
|
+
const serviceOperationReplacements = new Map();
|
|
15
|
+
const replacedOperationNamesServices = services.map((service) => {
|
|
16
|
+
serviceOperationReplacements.set(service.name, new Map());
|
|
17
|
+
return {
|
|
18
|
+
...service,
|
|
19
|
+
operations: service.operations.map((operation) => {
|
|
20
|
+
const operationReplacements = serviceOperationReplacements.get(service.name);
|
|
21
|
+
if (!operationReplacements)
|
|
22
|
+
throw new Error('operationReplacements not found');
|
|
23
|
+
const operationModifiers = operationNameModifiers.filter((modifier) => modifier.isPathMatch(operation.path) &&
|
|
24
|
+
(modifier.methods === undefined ||
|
|
25
|
+
modifier.methods.includes(operation.method)) &&
|
|
26
|
+
normalizeOperationNameModifierRegex(modifier.operationNameModifierRegex).test(operation.name));
|
|
27
|
+
const replacedOperationName = operationModifiers.reduce((operationName, modifier) => operationName.replace(normalizeOperationNameModifierRegex(modifier.operationNameModifierRegex), modifier.operationNameModifierReplace), operation.name);
|
|
28
|
+
if (replacedOperationName !== operation.name) {
|
|
29
|
+
if (!operationReplacements.has(replacedOperationName))
|
|
30
|
+
operationReplacements.set(replacedOperationName, new Map());
|
|
31
|
+
operationReplacements.get(replacedOperationName)?.set(operation.name, operationModifiers.map(({ isPathMatch: _, ...operationModifiersRest }) => operationModifiersRest));
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...operation,
|
|
35
|
+
name: replacedOperationName,
|
|
36
|
+
};
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
const errors = Array.from(serviceOperationReplacements).flatMap(([serviceName, modifiedOperations]) => Array.from(modifiedOperations).flatMap(([replacedOperationName, operationModifierMap]) => {
|
|
41
|
+
if (operationModifierMap.size > 1) {
|
|
42
|
+
return Array.from(operationModifierMap).map(([originalOperationName, modifiers]) => ({
|
|
43
|
+
serviceName,
|
|
44
|
+
replacedOperationName,
|
|
45
|
+
originalOperationName,
|
|
46
|
+
modifiers,
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
const [originalOperationName, modifiers] = Array.from(operationModifierMap).at(0) ?? [];
|
|
50
|
+
if (modifiers && modifiers.length > 1) {
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
serviceName,
|
|
54
|
+
replacedOperationName,
|
|
55
|
+
originalOperationName,
|
|
56
|
+
modifiers,
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
return [];
|
|
61
|
+
}));
|
|
62
|
+
return {
|
|
63
|
+
errors,
|
|
64
|
+
services: replacedOperationNamesServices,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
function normalizeOperationNameModifierRegex(regex) {
|
|
68
|
+
return new RegExp(regex, 'g');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parses the given options string into a structured format.
|
|
72
|
+
*
|
|
73
|
+
* @param optionItems - The configuration string to parse. Eg. `/foo,/bar/**:post[A-Z][A-Za-z]+sId[A-Z][A-Za-z]+ ==> createOne` or `post /** ==> createOne`
|
|
74
|
+
* @returns The parsed configuration.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* const parsedConfig = parseOperationNameModifier("/foo,/bar/**:/**:post[A-Z][A-Za-z]+sId[A-Z][A-Za-z]+ ==> createOne");
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export function parseOperationNameModifier(...optionItems) {
|
|
82
|
+
const globOperationNameModifierMap = [];
|
|
83
|
+
for (const optionItem of optionItems) {
|
|
84
|
+
const operationGlobsRaw = optionItem.slice(0, Math.max(0, optionItem.indexOf(':')));
|
|
85
|
+
if (!operationGlobsRaw.length)
|
|
86
|
+
throw new Error(`Invalid operation name modifier: ${optionItem}. Must be in the format of 'pathGlobs:operationNameModifierRegex ==> operationNameModifierReplace'`);
|
|
87
|
+
const modifierPattern = optionItem.slice(operationGlobsRaw.length + 1);
|
|
88
|
+
const patternRegex = modifierPattern.slice(0, modifierPattern.indexOf('==>'));
|
|
89
|
+
const patternReplace = modifierPattern.slice(modifierPattern.indexOf('==>') + '==>'.length + 1);
|
|
90
|
+
const { methods, pathGlobs } = parseOperationGlobs(operationGlobsRaw);
|
|
91
|
+
if (!splitCommaSeparatedGlobs(pathGlobs).length)
|
|
92
|
+
throw new Error(`Invalid path glob: ${pathGlobs}. Must be a comma-separated list of globs.`);
|
|
93
|
+
globOperationNameModifierMap.push({
|
|
94
|
+
methods,
|
|
95
|
+
pathGlobs,
|
|
96
|
+
operationNameModifierRegex: patternRegex.trim(),
|
|
97
|
+
operationNameModifierReplace: patternReplace.trim(),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return globOperationNameModifierMap;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=processOperationNameModifierOption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processOperationNameModifierOption.js","sourceRoot":"","sources":["../../src/lib/processOperationNameModifierOption.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a comma-separated list of globs
|
|
3
|
+
* @param commaSeparatedGlobs - Example: `/foo , /bar/**, ,`
|
|
4
|
+
* @returns Example: `/foo,/bar/**`
|
|
5
|
+
*/
|
|
6
|
+
export declare function splitCommaSeparatedGlobs(commaSeparatedGlobs: string | undefined): string[];
|
|
7
|
+
//# sourceMappingURL=splitCommaSeparatedGlobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splitCommaSeparatedGlobs.d.ts","sourceRoot":"","sources":["../../src/lib/splitCommaSeparatedGlobs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,mBAAmB,EAAE,MAAM,GAAG,SAAS,GACtC,MAAM,EAAE,CAQV"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a comma-separated list of globs
|
|
3
|
+
* @param commaSeparatedGlobs - Example: `/foo , /bar/**, ,`
|
|
4
|
+
* @returns Example: `/foo,/bar/**`
|
|
5
|
+
*/
|
|
6
|
+
export function splitCommaSeparatedGlobs(commaSeparatedGlobs) {
|
|
7
|
+
if (commaSeparatedGlobs === undefined)
|
|
8
|
+
return [];
|
|
9
|
+
if (typeof commaSeparatedGlobs !== 'string')
|
|
10
|
+
throw new Error('Invalid input');
|
|
11
|
+
return commaSeparatedGlobs
|
|
12
|
+
.split(',')
|
|
13
|
+
.map((path) => path.trim())
|
|
14
|
+
.filter(Boolean);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=splitCommaSeparatedGlobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splitCommaSeparatedGlobs.js","sourceRoot":"","sources":["../../src/lib/splitCommaSeparatedGlobs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,mBAAuC;IAEvC,IAAI,mBAAmB,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACjD,IAAI,OAAO,mBAAmB,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IAE9E,OAAO,mBAAmB;SACvB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openapi-qraft/plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0-beta.0",
|
|
4
4
|
"packageManager": "yarn@4.0.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc --project tsconfig.build.json",
|
|
8
8
|
"dev": "yarn build --watch --noEmitOnError false",
|
|
9
|
-
"test": "vitest run",
|
|
9
|
+
"test": "vitest run --coverage",
|
|
10
10
|
"typecheck": "tsc --noEmit",
|
|
11
11
|
"lint": "eslint",
|
|
12
12
|
"clean": "rimraf dist/"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"camelcase": "^8.0.0",
|
|
29
29
|
"commander": "^11.1.0",
|
|
30
30
|
"micromatch": "^4.0.5",
|
|
31
|
-
"openapi-typescript": "7.
|
|
31
|
+
"openapi-typescript": "^7.3.0",
|
|
32
32
|
"ora": "^8.0.1",
|
|
33
33
|
"yaml": "^2.3.4"
|
|
34
34
|
},
|
package/src/lib/QraftCommand.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { pathToFileURL, URL } from 'node:url';
|
|
|
7
7
|
import c from 'ansi-colors';
|
|
8
8
|
import { Command } from 'commander';
|
|
9
9
|
import ora, { Ora } from 'ora';
|
|
10
|
-
import { parsePathGlobs } from './createServicePathMatch.js';
|
|
11
10
|
import { filterDocumentPaths } from './filterDocumentPaths.js';
|
|
12
11
|
import { GeneratorFile } from './GeneratorFile.js';
|
|
13
12
|
import { handleSchemaInput } from './handleSchemaInput.js';
|
|
@@ -16,10 +15,15 @@ import { OpenAPISchemaType } from './open-api/OpenAPISchemaType.js';
|
|
|
16
15
|
import { readSchema } from './open-api/readSchema.js';
|
|
17
16
|
import { OutputOptions } from './OutputOptions.js';
|
|
18
17
|
import {
|
|
19
|
-
|
|
18
|
+
createPredefinedParametersGlobs,
|
|
20
19
|
parseOperationPredefinedParametersOption,
|
|
21
20
|
predefineSchemaParameters,
|
|
22
21
|
} from './predefineSchemaParameters.js';
|
|
22
|
+
import {
|
|
23
|
+
parseOperationNameModifier,
|
|
24
|
+
processOperationNameModifierOption,
|
|
25
|
+
} from './processOperationNameModifierOption.js';
|
|
26
|
+
import { splitCommaSeparatedGlobs } from './splitCommaSeparatedGlobs.js';
|
|
23
27
|
import { writeGeneratorFiles } from './writeGeneratorFiles.js';
|
|
24
28
|
|
|
25
29
|
export class QraftCommand extends Command {
|
|
@@ -50,7 +54,11 @@ export class QraftCommand extends Command {
|
|
|
50
54
|
)
|
|
51
55
|
.option(
|
|
52
56
|
'--operation-predefined-parameters <patterns...>',
|
|
53
|
-
'Predefined parameters for services. The specified services parameters will be optional. Eg: "/**:header.x-monite-version,query.x-api-key
|
|
57
|
+
'Predefined parameters for services. The specified services parameters will be optional. Eg: "/**:header.x-monite-version,query.x-api-key" or "get /**:header.x-monite-entity-id"'
|
|
58
|
+
)
|
|
59
|
+
.option(
|
|
60
|
+
'--operation-name-modifier <patterns...>',
|
|
61
|
+
'Modifies operation names using a pattern. Use the `==>` operator to separate the regular expression (left) and the substitution string (right). For example: "post /**:[A-Za-z]+Id ==> createOne"'
|
|
54
62
|
)
|
|
55
63
|
.option(
|
|
56
64
|
'--postfix-services <string>',
|
|
@@ -103,20 +111,20 @@ export class QraftCommand extends Command {
|
|
|
103
111
|
spinner.text = 'Reading OpenAPI Schema';
|
|
104
112
|
let schema = filterDocumentPaths(
|
|
105
113
|
await readSchema(input),
|
|
106
|
-
|
|
114
|
+
splitCommaSeparatedGlobs(args.filterServices)
|
|
107
115
|
);
|
|
108
116
|
|
|
109
117
|
if (args.operationPredefinedParameters) {
|
|
110
|
-
const
|
|
118
|
+
const predefinedParametersGlobs = createPredefinedParametersGlobs(
|
|
111
119
|
schema,
|
|
112
120
|
parseOperationPredefinedParametersOption(
|
|
113
121
|
...args.operationPredefinedParameters
|
|
114
122
|
)
|
|
115
123
|
);
|
|
116
124
|
|
|
117
|
-
const predefinedParametersErrors =
|
|
118
|
-
|
|
119
|
-
)
|
|
125
|
+
const predefinedParametersErrors = predefinedParametersGlobs.flatMap(
|
|
126
|
+
({ errors }) => errors
|
|
127
|
+
);
|
|
120
128
|
|
|
121
129
|
if (predefinedParametersErrors.length) {
|
|
122
130
|
predefinedParametersErrors.forEach((error) => spinner.warn(error));
|
|
@@ -126,15 +134,56 @@ export class QraftCommand extends Command {
|
|
|
126
134
|
process.exit(1);
|
|
127
135
|
}
|
|
128
136
|
|
|
129
|
-
schema = predefineSchemaParameters(schema,
|
|
137
|
+
schema = predefineSchemaParameters(schema, predefinedParametersGlobs);
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
spinner.text = 'Getting OpenAPI Services';
|
|
133
|
-
|
|
141
|
+
let services = getServices(schema as OpenAPISchemaType, {
|
|
134
142
|
postfixServices: args.postfixServices,
|
|
135
143
|
serviceNameBase: args.serviceNameBase,
|
|
136
144
|
});
|
|
137
145
|
|
|
146
|
+
if (args.operationNameModifier) {
|
|
147
|
+
const {
|
|
148
|
+
services: modifiedServicesOperationNames,
|
|
149
|
+
errors: operationNameModifierErrors,
|
|
150
|
+
} = processOperationNameModifierOption(
|
|
151
|
+
parseOperationNameModifier(
|
|
152
|
+
...(Array.isArray(args.operationNameModifier)
|
|
153
|
+
? args.operationNameModifier
|
|
154
|
+
: [args.operationNameModifier])
|
|
155
|
+
),
|
|
156
|
+
services
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (operationNameModifierErrors.length) {
|
|
160
|
+
operationNameModifierErrors.forEach((error) => {
|
|
161
|
+
spinner.fail(
|
|
162
|
+
[
|
|
163
|
+
c.red.bold(
|
|
164
|
+
`Error occurred during operation name modifier setup: '${error.serviceName}.${error.originalOperationName}' to '${error.replacedOperationName}'`
|
|
165
|
+
),
|
|
166
|
+
c.yellow(
|
|
167
|
+
error.modifiers
|
|
168
|
+
.map(
|
|
169
|
+
({
|
|
170
|
+
pathGlobs,
|
|
171
|
+
operationNameModifierRegex,
|
|
172
|
+
operationNameModifierReplace,
|
|
173
|
+
}) =>
|
|
174
|
+
`Conflicting operation name modifier: '${pathGlobs}': '${operationNameModifierRegex}' => '${operationNameModifierReplace}'`
|
|
175
|
+
)
|
|
176
|
+
.join('\n')
|
|
177
|
+
),
|
|
178
|
+
].join('\n')
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
services = modifiedServicesOperationNames;
|
|
185
|
+
}
|
|
186
|
+
|
|
138
187
|
spinner.text = 'Generating code';
|
|
139
188
|
|
|
140
189
|
const outputDir = normalizeOutputDirPath(args.outputDir);
|
|
@@ -26,21 +26,3 @@ export const createServicePathMatch = (servicesGlob: string[]) => {
|
|
|
26
26
|
});
|
|
27
27
|
};
|
|
28
28
|
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Parse services filter option to array
|
|
32
|
-
* @example
|
|
33
|
-
* ```ts
|
|
34
|
-
* parsePathGlobs('/user/**,/post/**')
|
|
35
|
-
* ```
|
|
36
|
-
* @param endpointGlobs
|
|
37
|
-
*/
|
|
38
|
-
export function parsePathGlobs(endpointGlobs: string): string[] | undefined {
|
|
39
|
-
return endpointGlobs
|
|
40
|
-
? endpointGlobs
|
|
41
|
-
.split(',')
|
|
42
|
-
.filter((item: unknown): item is string =>
|
|
43
|
-
Boolean(typeof item === 'string' && item.trim())
|
|
44
|
-
)
|
|
45
|
-
: undefined;
|
|
46
|
-
}
|
|
@@ -13,9 +13,9 @@ import { createServicePathMatch } from './createServicePathMatch.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export const filterDocumentPaths = (
|
|
15
15
|
schema: OpenAPI3,
|
|
16
|
-
servicesGlob: string[]
|
|
16
|
+
servicesGlob: string[]
|
|
17
17
|
): OpenAPI3 => {
|
|
18
|
-
if (!servicesGlob) return schema;
|
|
18
|
+
if (!servicesGlob.length) return schema;
|
|
19
19
|
|
|
20
20
|
const isPathMatch = createServicePathMatch(servicesGlob);
|
|
21
21
|
const paths: typeof schema.paths = {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ServiceOperation } from './open-api/getServices.js';
|
|
2
|
+
import { splitCommaSeparatedGlobs } from './splitCommaSeparatedGlobs.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param operationFullGlobs - Example: `post /**`, `/**,/foo/bar`
|
|
7
|
+
* @returns `pathGlobs`, and `methods` as an array if methods are specified, or `undefined`
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* parseOperationGlobs('post /**');
|
|
11
|
+
* parseOperationGlobs('patch,put /foo/bar');
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export const parseOperationGlobs = (operationFullGlobs: string) => {
|
|
15
|
+
const methodsGlobsRaw = operationFullGlobs.slice(
|
|
16
|
+
0,
|
|
17
|
+
Math.max(0, operationFullGlobs.indexOf('/'))
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const methods = methodsGlobsRaw
|
|
21
|
+
.split(',')
|
|
22
|
+
.map((method) => method.trim().toLowerCase())
|
|
23
|
+
.filter(isSupportedOperationMethod)
|
|
24
|
+
.toSorted(sortSupportedMethods);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
methods: methods.length ? methods : undefined,
|
|
28
|
+
pathGlobs: splitCommaSeparatedGlobs(
|
|
29
|
+
operationFullGlobs.slice(methodsGlobsRaw.length)
|
|
30
|
+
).join(','),
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const supportedOperationMethods = Object.values({
|
|
35
|
+
get: 'get',
|
|
36
|
+
post: 'post',
|
|
37
|
+
put: 'put',
|
|
38
|
+
patch: 'patch',
|
|
39
|
+
delete: 'delete',
|
|
40
|
+
options: 'options',
|
|
41
|
+
head: 'head',
|
|
42
|
+
trace: 'trace',
|
|
43
|
+
} satisfies { [Method in OperationGlobMethods]: Method });
|
|
44
|
+
|
|
45
|
+
const isSupportedOperationMethod = (
|
|
46
|
+
operationRaw: string
|
|
47
|
+
): operationRaw is OperationGlobMethods =>
|
|
48
|
+
supportedOperationMethods.includes(operationRaw as OperationGlobMethods);
|
|
49
|
+
|
|
50
|
+
const sortSupportedMethods = (a: string, b: string) =>
|
|
51
|
+
supportedOperationMethods.indexOf(a as OperationGlobMethods) -
|
|
52
|
+
supportedOperationMethods.indexOf(b as OperationGlobMethods);
|
|
53
|
+
|
|
54
|
+
export type OperationGlobMethods = ServiceOperation['method'];
|
|
@@ -4,14 +4,16 @@ import {
|
|
|
4
4
|
PathItemObject,
|
|
5
5
|
ReferenceObject,
|
|
6
6
|
} from 'openapi-typescript/src/types.js';
|
|
7
|
+
import { createServicePathMatch } from './createServicePathMatch.js';
|
|
7
8
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from './
|
|
9
|
+
OperationGlobMethods,
|
|
10
|
+
parseOperationGlobs,
|
|
11
|
+
} from './parseOperationGlobs.js';
|
|
12
|
+
import { splitCommaSeparatedGlobs } from './splitCommaSeparatedGlobs.js';
|
|
11
13
|
|
|
12
14
|
export function predefineSchemaParameters(
|
|
13
15
|
schema: OpenAPI3,
|
|
14
|
-
|
|
16
|
+
predefinedParametersGlobs: PredefinedParametersGlob[]
|
|
15
17
|
): OpenAPI3 {
|
|
16
18
|
const predefinedSchemaPaths: typeof schema.paths = {};
|
|
17
19
|
|
|
@@ -31,9 +33,7 @@ export function predefineSchemaParameters(
|
|
|
31
33
|
const operation = service[method];
|
|
32
34
|
assertIsOperationObject(operation);
|
|
33
35
|
|
|
34
|
-
for (const predefinedParametersGlob of
|
|
35
|
-
predefinedParametersGlobMap
|
|
36
|
-
)) {
|
|
36
|
+
for (const predefinedParametersGlob of predefinedParametersGlobs) {
|
|
37
37
|
if (!predefinedParametersGlob.paths.includes(path)) {
|
|
38
38
|
continue;
|
|
39
39
|
}
|
|
@@ -82,11 +82,6 @@ export function predefineSchemaParameters(
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
type PredefinedParametersOptionMap = Record<
|
|
86
|
-
string,
|
|
87
|
-
Pick<ParameterObject, 'in' | 'name'>[]
|
|
88
|
-
>;
|
|
89
|
-
|
|
90
85
|
/**
|
|
91
86
|
* Parses the given configuration string into a structured format.
|
|
92
87
|
*
|
|
@@ -100,80 +95,90 @@ type PredefinedParametersOptionMap = Record<
|
|
|
100
95
|
*/
|
|
101
96
|
function parseOperationPredefinedParametersOption(
|
|
102
97
|
...optionItems: string[]
|
|
103
|
-
):
|
|
104
|
-
const globParametersMap
|
|
98
|
+
): ParsedPredefinedParametersOption[] {
|
|
99
|
+
const globParametersMap = new Map<string, ParsedPredefinedParametersOption>();
|
|
105
100
|
|
|
106
101
|
for (const optionItem of optionItems) {
|
|
107
|
-
const
|
|
102
|
+
const operationGlobsRaw = optionItem.slice(
|
|
103
|
+
0,
|
|
104
|
+
Math.max(0, optionItem.indexOf(':')) || optionItem.length // the second part after the `:` is optional for the predefined parameters option
|
|
105
|
+
);
|
|
106
|
+
const parameterGlobs = optionItem.slice(operationGlobsRaw.length + 1);
|
|
107
|
+
|
|
108
108
|
const parameters: Record<'header' | 'query' | 'cookie', string[]> = {
|
|
109
109
|
header: [],
|
|
110
110
|
query: [],
|
|
111
111
|
cookie: [],
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
.split('
|
|
117
|
-
.map((option) => option.trim())
|
|
118
|
-
.filter(Boolean)
|
|
119
|
-
.forEach((option) => {
|
|
120
|
-
const [type, key] = option.split('.').map((s) => s.trim());
|
|
121
|
-
|
|
122
|
-
if (type !== 'header' && type !== 'query' && type !== 'cookie')
|
|
123
|
-
throw new Error(
|
|
124
|
-
`Invalid option type: ${type} in ${option}. Must be one of 'header', 'query', or 'cookie'`
|
|
125
|
-
);
|
|
114
|
+
if (parameterGlobs) {
|
|
115
|
+
splitCommaSeparatedGlobs(parameterGlobs).forEach((option) => {
|
|
116
|
+
const [type, key] = option.split('.').map((s) => s.trim());
|
|
126
117
|
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
if (type !== 'header' && type !== 'query' && type !== 'cookie')
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Invalid option type: ${type} in ${option}. Must be one of 'header', 'query', or 'cookie'`
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!parameters[type].includes(key)) parameters[type].push(key);
|
|
124
|
+
});
|
|
129
125
|
}
|
|
130
126
|
|
|
131
|
-
|
|
132
|
-
|
|
127
|
+
const { pathGlobs, methods: methodList } =
|
|
128
|
+
parseOperationGlobs(operationGlobsRaw);
|
|
129
|
+
|
|
130
|
+
const methods = methodList ? methodList.join(',') : '';
|
|
131
|
+
|
|
132
|
+
const globKey = `${methods}${pathGlobs}`;
|
|
133
|
+
|
|
134
|
+
if (globParametersMap.has(globKey)) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Duplicate path: ${methods ? methods + ' ' : ''}${pathGlobs} in config string.`
|
|
137
|
+
);
|
|
133
138
|
}
|
|
134
139
|
|
|
135
|
-
globParametersMap
|
|
136
|
-
([inLocation, names]) =>
|
|
140
|
+
globParametersMap.set(globKey, {
|
|
141
|
+
parameters: Object.entries(parameters).flatMap(([inLocation, names]) =>
|
|
137
142
|
names.map((name) => ({ in: inLocation as ParameterObject['in'], name }))
|
|
138
|
-
|
|
143
|
+
),
|
|
144
|
+
methods: methodList,
|
|
145
|
+
pathGlobs,
|
|
146
|
+
});
|
|
139
147
|
}
|
|
140
148
|
|
|
141
|
-
return globParametersMap;
|
|
149
|
+
return Array.from(globParametersMap.values());
|
|
142
150
|
}
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
[glob: string]: {
|
|
146
|
-
paths: string[];
|
|
147
|
-
parameters: ParameterObject[];
|
|
148
|
-
errors: string[];
|
|
149
|
-
};
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
export function createPredefinedParametersGlobMap(
|
|
152
|
+
export function createPredefinedParametersGlobs(
|
|
153
153
|
schema: OpenAPI3,
|
|
154
|
-
predefinedParameters:
|
|
155
|
-
):
|
|
156
|
-
const predefinedParametersGlobMap
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
154
|
+
predefinedParameters: ParsedPredefinedParametersOption[]
|
|
155
|
+
): Array<PredefinedParametersGlob> {
|
|
156
|
+
const predefinedParametersGlobMap = new Map<
|
|
157
|
+
string,
|
|
158
|
+
PredefinedParametersGlob
|
|
159
|
+
>();
|
|
160
|
+
|
|
161
|
+
const servicePredefinedParametersList = predefinedParameters.map(
|
|
162
|
+
({ pathGlobs, parameters, methods }) => {
|
|
163
|
+
const globs = splitCommaSeparatedGlobs(pathGlobs);
|
|
164
|
+
if (!globs.length)
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Invalid path glob: ${pathGlobs}. Must be a comma-separated list of globs.`
|
|
167
|
+
);
|
|
166
168
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
return {
|
|
170
|
+
methods,
|
|
171
|
+
pathGlobs,
|
|
172
|
+
parameters,
|
|
173
|
+
isPathMatch: createServicePathMatch(globs),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
);
|
|
173
177
|
|
|
174
178
|
for (const path in schema.paths) {
|
|
175
179
|
if (!Object.prototype.hasOwnProperty.call(schema.paths, path)) continue;
|
|
176
180
|
for (const {
|
|
181
|
+
methods,
|
|
177
182
|
pathGlobs,
|
|
178
183
|
parameters: parametersToPredefine,
|
|
179
184
|
isPathMatch,
|
|
@@ -187,6 +192,11 @@ export function createPredefinedParametersGlobMap(
|
|
|
187
192
|
for (const _method in service) {
|
|
188
193
|
const method = _method as keyof typeof service;
|
|
189
194
|
if (!Object.prototype.hasOwnProperty.call(service, method)) continue;
|
|
195
|
+
if (
|
|
196
|
+
methods !== undefined &&
|
|
197
|
+
!methods.includes(method as OperationGlobMethods)
|
|
198
|
+
)
|
|
199
|
+
continue;
|
|
190
200
|
|
|
191
201
|
const operation = service[method];
|
|
192
202
|
assertIsOperationObject(operation);
|
|
@@ -194,14 +204,18 @@ export function createPredefinedParametersGlobMap(
|
|
|
194
204
|
const operationParameters = operation.parameters ?? [];
|
|
195
205
|
assertIsParameterObjects(operationParameters);
|
|
196
206
|
|
|
197
|
-
if (!(pathGlobs
|
|
198
|
-
predefinedParametersGlobMap
|
|
207
|
+
if (!predefinedParametersGlobMap.has(pathGlobs))
|
|
208
|
+
predefinedParametersGlobMap.set(pathGlobs, {
|
|
209
|
+
methods: [],
|
|
199
210
|
paths: [],
|
|
200
211
|
parameters: [],
|
|
201
212
|
errors: [],
|
|
202
|
-
};
|
|
213
|
+
});
|
|
203
214
|
|
|
204
|
-
const predefinedPathGlob = predefinedParametersGlobMap
|
|
215
|
+
const predefinedPathGlob = predefinedParametersGlobMap.get(pathGlobs);
|
|
216
|
+
|
|
217
|
+
if (!predefinedPathGlob)
|
|
218
|
+
throw new Error('predefinedPathGlob not found');
|
|
205
219
|
|
|
206
220
|
for (const parameterSchemaToPredefine of parametersToPredefine) {
|
|
207
221
|
const parameterToPredefine = findPredefinedOperationParameter(
|
|
@@ -209,6 +223,15 @@ export function createPredefinedParametersGlobMap(
|
|
|
209
223
|
operationParameters
|
|
210
224
|
);
|
|
211
225
|
|
|
226
|
+
if (
|
|
227
|
+
!predefinedPathGlob.methods?.includes(
|
|
228
|
+
method as OperationGlobMethods
|
|
229
|
+
)
|
|
230
|
+
) {
|
|
231
|
+
if (!predefinedPathGlob.methods) predefinedPathGlob.methods = [];
|
|
232
|
+
predefinedPathGlob.methods.push(method as OperationGlobMethods);
|
|
233
|
+
}
|
|
234
|
+
|
|
212
235
|
if (!parameterToPredefine) {
|
|
213
236
|
predefinedPathGlob.errors.push(
|
|
214
237
|
`Missing predefined parameter '${parameterSchemaToPredefine.in}' '${parameterSchemaToPredefine.name}' in '${method} ${path}' in '${pathGlobs}'`
|
|
@@ -246,17 +269,18 @@ export function createPredefinedParametersGlobMap(
|
|
|
246
269
|
}
|
|
247
270
|
}
|
|
248
271
|
|
|
249
|
-
|
|
250
|
-
if (!predefinedParametersGlobMap
|
|
251
|
-
predefinedParametersGlobMap
|
|
272
|
+
predefinedParameters.forEach(({ pathGlobs, methods }) => {
|
|
273
|
+
if (!predefinedParametersGlobMap.has(pathGlobs)) {
|
|
274
|
+
predefinedParametersGlobMap.set(pathGlobs, {
|
|
275
|
+
methods,
|
|
252
276
|
paths: [],
|
|
253
277
|
parameters: [],
|
|
254
278
|
errors: [`No matching paths found for '${pathGlobs}'`],
|
|
255
|
-
};
|
|
279
|
+
});
|
|
256
280
|
}
|
|
257
281
|
});
|
|
258
282
|
|
|
259
|
-
return predefinedParametersGlobMap;
|
|
283
|
+
return Array.from(predefinedParametersGlobMap.values());
|
|
260
284
|
}
|
|
261
285
|
|
|
262
286
|
function findPredefinedOperationParameter(
|
|
@@ -307,4 +331,17 @@ export function assertIsParameterObjects(
|
|
|
307
331
|
});
|
|
308
332
|
}
|
|
309
333
|
|
|
334
|
+
type ParsedPredefinedParametersOption = {
|
|
335
|
+
methods: OperationGlobMethods[] | undefined;
|
|
336
|
+
pathGlobs: string;
|
|
337
|
+
parameters: Pick<ParameterObject, 'in' | 'name'>[];
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
type PredefinedParametersGlob = {
|
|
341
|
+
methods: OperationGlobMethods[] | undefined;
|
|
342
|
+
paths: string[];
|
|
343
|
+
parameters: Pick<ParameterObject, 'in' | 'name'>[];
|
|
344
|
+
errors: string[];
|
|
345
|
+
};
|
|
346
|
+
|
|
310
347
|
export { parseOperationPredefinedParametersOption };
|