@openapi-qraft/plugin 1.8.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/README.md +1 -0
- package/dist/lib/GeneratorFile.d.ts +9 -0
- package/dist/lib/GeneratorFile.js +2 -0
- package/dist/lib/GeneratorFile.js.map +1 -0
- package/dist/lib/OutputOptions.d.ts +6 -0
- package/dist/lib/OutputOptions.js +2 -0
- package/dist/lib/OutputOptions.js.map +1 -0
- package/dist/lib/QraftCommand.d.ts +35 -0
- package/dist/lib/QraftCommand.js +103 -0
- package/dist/lib/QraftCommand.js.map +1 -0
- package/dist/lib/QraftCommandPlugin.d.ts +4 -0
- package/dist/lib/QraftCommandPlugin.js +2 -0
- package/dist/lib/QraftCommandPlugin.js.map +1 -0
- package/dist/lib/fileHeader.d.ts +1 -0
- package/dist/lib/fileHeader.js +3 -0
- package/dist/lib/fileHeader.js.map +1 -0
- package/dist/lib/open-api/OpenAPISchemaType.d.ts +31 -0
- package/dist/lib/open-api/OpenAPISchemaType.js +2 -0
- package/dist/lib/open-api/OpenAPISchemaType.js.map +1 -0
- package/dist/lib/open-api/getContent.d.ts +5 -0
- package/dist/lib/open-api/getContent.js +23 -0
- package/dist/lib/open-api/getContent.js.map +1 -0
- package/dist/lib/open-api/getDocumentServices.d.ts +12 -0
- package/dist/lib/open-api/getDocumentServices.js +7 -0
- package/dist/lib/open-api/getDocumentServices.js.map +1 -0
- package/dist/lib/open-api/getOperationName.d.ts +6 -0
- package/dist/lib/open-api/getOperationName.js +20 -0
- package/dist/lib/open-api/getOperationName.js.map +1 -0
- package/dist/lib/open-api/getServiceName.d.ts +5 -0
- package/dist/lib/open-api/getServiceName.js +13 -0
- package/dist/lib/open-api/getServiceName.js.map +1 -0
- package/dist/lib/open-api/getServiceNamesByOperationEndpoint.d.ts +4 -0
- package/dist/lib/open-api/getServiceNamesByOperationEndpoint.js +30 -0
- package/dist/lib/open-api/getServiceNamesByOperationEndpoint.js.map +1 -0
- package/dist/lib/open-api/getServiceNamesByOperationTags.d.ts +1 -0
- package/dist/lib/open-api/getServiceNamesByOperationTags.js +11 -0
- package/dist/lib/open-api/getServiceNamesByOperationTags.js.map +1 -0
- package/dist/lib/open-api/getServices.d.ts +39 -0
- package/dist/lib/open-api/getServices.js +112 -0
- package/dist/lib/open-api/getServices.js.map +1 -0
- package/dist/lib/open-api/readSchema.d.ts +7 -0
- package/dist/lib/open-api/readSchema.js +15 -0
- package/dist/lib/open-api/readSchema.js.map +1 -0
- package/dist/lib/writeGeneratorFiles.d.ts +6 -0
- package/dist/lib/writeGeneratorFiles.js +51 -0
- package/dist/lib/writeGeneratorFiles.js.map +1 -0
- package/package.json +53 -0
- package/src/lib/GeneratorFile.ts +5 -0
- package/src/lib/OutputOptions.ts +6 -0
- package/src/lib/QraftCommand.ts +192 -0
- package/src/lib/QraftCommandPlugin.ts +5 -0
- package/src/lib/fileHeader.ts +2 -0
- package/src/lib/open-api/OpenAPISchemaType.ts +31 -0
- package/src/lib/open-api/getContent.ts +26 -0
- package/src/lib/open-api/getDocumentServices.ts +19 -0
- package/src/lib/open-api/getOperationName.ts +28 -0
- package/src/lib/open-api/getServiceName.ts +14 -0
- package/src/lib/open-api/getServiceNamesByOperationEndpoint.ts +70 -0
- package/src/lib/open-api/getServiceNamesByOperationTags.ts +15 -0
- package/src/lib/open-api/getServices.ts +208 -0
- package/src/lib/open-api/readSchema.ts +27 -0
- package/src/lib/writeGeneratorFiles.ts +83 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readSchema.js","sourceRoot":"","sources":["../../../src/lib/open-api/readSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAIzE,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,MAAmD,EACnD,EAAE;IACF,MAAM,KAAK,GAAG,MAAM,YAAY,CAC9B;QACE,KAAK,EAAE;YACL,8BAA8B,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,wCAAwC;SAChG;KACF,EACD,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE,CACzB,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE;QAC7C,KAAK;QACL,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IAEH,OAAO,MAA2B,CAAC;AACrC,CAAC,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import c from 'ansi-colors';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
export const writeGeneratorFiles = async ({ spinner, fileItems, }) => {
|
|
4
|
+
await setupDirectories(spinner, fileItems);
|
|
5
|
+
await writeFiles(spinner, fileItems);
|
|
6
|
+
};
|
|
7
|
+
const setupDirectories = async (spinner, fileItems) => {
|
|
8
|
+
for (const fileItem of fileItems) {
|
|
9
|
+
if (!('directory' in fileItem))
|
|
10
|
+
continue;
|
|
11
|
+
if (fileItem.clean) {
|
|
12
|
+
spinner.text = `Cleaning ${c.magenta(fileItem.directory.pathname)}`;
|
|
13
|
+
try {
|
|
14
|
+
await fs.promises.rm(fileItem.directory, {
|
|
15
|
+
recursive: true,
|
|
16
|
+
force: true,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
spinner.fail(c.redBright(`Error occurred during ${c.magenta(fileItem.directory.pathname)} directory cleaning`));
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
spinner.text = `Creating directory ${c.magenta(fileItem.directory.pathname)}`;
|
|
26
|
+
await fs.promises.mkdir(fileItem.directory, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
spinner.fail(c.redBright(`Error occurred during ${c.magenta(fileItem.directory.pathname)} directory creation`));
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const writeFiles = async (spinner, fileItems) => {
|
|
35
|
+
for (const fileItem of fileItems) {
|
|
36
|
+
if ('directory' in fileItem)
|
|
37
|
+
continue;
|
|
38
|
+
const { file, code } = fileItem;
|
|
39
|
+
spinner.text = `Writing ${c.magenta(file.pathname)}`;
|
|
40
|
+
try {
|
|
41
|
+
await fs.promises.writeFile(file, code, {
|
|
42
|
+
encoding: 'utf-8',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
spinner.fail(c.redBright(`Error occurred during ${c.magenta(file.pathname)} file writing`));
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=writeGeneratorFiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writeGeneratorFiles.js","sourceRoot":"","sources":["../../src/lib/writeGeneratorFiles.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,aAAa,CAAC;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAC;AAKzB,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EAAE,EACxC,OAAO,EACP,SAAS,GAIV,EAAE,EAAE;IACH,MAAM,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC3C,MAAM,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,KAAK,EAAE,OAAY,EAAE,SAA0B,EAAE,EAAE;IAC1E,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC;YAAE,SAAS;QAEzC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;YACnB,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YAEpE,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE;oBACvC,SAAS,EAAE,IAAI;oBACf,KAAK,EAAE,IAAI;iBACZ,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CACV,CAAC,CAAC,SAAS,CACT,yBAAyB,CAAC,CAAC,OAAO,CAChC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAC5B,qBAAqB,CACvB,CACF,CAAC;gBAEF,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,GAAG,sBAAsB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YAE9E,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CACV,CAAC,CAAC,SAAS,CACT,yBAAyB,CAAC,CAAC,OAAO,CAChC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAC5B,qBAAqB,CACvB,CACF,CAAC;YAEF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,KAAK,EAAE,OAAY,EAAE,SAA0B,EAAE,EAAE;IACpE,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,WAAW,IAAI,QAAQ;YAAE,SAAS;QAEtC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;QAEhC,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAErD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE;gBACtC,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CACV,CAAC,CAAC,SAAS,CACT,yBAAyB,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CACjE,CACF,CAAC;YAEF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openapi-qraft/plugin",
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"packageManager": "yarn@4.0.2",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "rimraf dist/ && tsc --project tsconfig.build.json",
|
|
8
|
+
"dev": "yarn build --watch",
|
|
9
|
+
"test": "vitest run",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"lint": "eslint"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
"./package.json": "./package.json",
|
|
15
|
+
"./*": "./dist/*.js"
|
|
16
|
+
},
|
|
17
|
+
"typesVersions": {
|
|
18
|
+
"*": {
|
|
19
|
+
"*": [
|
|
20
|
+
"dist/*"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"ansi-colors": "^4.1.3",
|
|
26
|
+
"camelcase": "^8.0.0",
|
|
27
|
+
"micromatch": "^4.0.5",
|
|
28
|
+
"openapi-typescript": "7.0.0-next.7",
|
|
29
|
+
"ora": "^8.0.1",
|
|
30
|
+
"yaml": "^2.3.4"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@openapi-qraft/test-fixtures": "~1.0.0",
|
|
34
|
+
"@types/micromatch": "^4",
|
|
35
|
+
"@types/node": "^18.19.4",
|
|
36
|
+
"commander": "^11.1.0",
|
|
37
|
+
"eslint": "^9.0.0",
|
|
38
|
+
"rimraf": "^5.0.5",
|
|
39
|
+
"typescript": "^5.3.3",
|
|
40
|
+
"vitest": "^1.4.0"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"src",
|
|
45
|
+
"!dist/**/*.test.*",
|
|
46
|
+
"!dist/**/*.spec.*",
|
|
47
|
+
"!src/**/*.test.*",
|
|
48
|
+
"!src/**/*.spec.*"
|
|
49
|
+
],
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import c from 'ansi-colors';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { sep } from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
import { pathToFileURL, URL } from 'node:url';
|
|
6
|
+
import ora, { Ora } from 'ora';
|
|
7
|
+
|
|
8
|
+
import { GeneratorFile } from './GeneratorFile.js';
|
|
9
|
+
import { getDocumentServices } from './open-api/getDocumentServices.js';
|
|
10
|
+
import { Service } from './open-api/getServices.js';
|
|
11
|
+
import { OutputOptions } from './OutputOptions.js';
|
|
12
|
+
import { writeGeneratorFiles } from './writeGeneratorFiles.js';
|
|
13
|
+
|
|
14
|
+
export class QraftCommand extends Command {
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
|
|
18
|
+
this.usage('[input] [options]')
|
|
19
|
+
.argument(
|
|
20
|
+
'[input]',
|
|
21
|
+
'Input OpenAPI Schema file path, URL (json, yml)',
|
|
22
|
+
null
|
|
23
|
+
)
|
|
24
|
+
.requiredOption(
|
|
25
|
+
'-o, --output-dir <path>',
|
|
26
|
+
'Output directory for generated services'
|
|
27
|
+
)
|
|
28
|
+
.option(
|
|
29
|
+
'-rm, --clean',
|
|
30
|
+
'Clean output directory before generating services'
|
|
31
|
+
)
|
|
32
|
+
.option(
|
|
33
|
+
'--filter-services <glob-pattern>',
|
|
34
|
+
'Filter services to be generated by glob pattern. Eg: "/user/**,/post/**". See NPM `micromatch` package for more details.'
|
|
35
|
+
)
|
|
36
|
+
.option(
|
|
37
|
+
'--postfix-services <string>',
|
|
38
|
+
'Postfix to be added to the generated service name (eg: Service)'
|
|
39
|
+
)
|
|
40
|
+
.option(
|
|
41
|
+
'--service-name-base <endpoint[<index>] | tags>',
|
|
42
|
+
'Use OpenAPI Operation `endpoint[<index>]` path part (e.g.: "/0/1/2") or `tags` as the base name of the service.',
|
|
43
|
+
'endpoint[0]'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
action(callback: QraftCommandActionCallback): this {
|
|
48
|
+
return super.action(async (...actionArgs) => {
|
|
49
|
+
const { version: packageVersion } = await import(
|
|
50
|
+
'@openapi-qraft/plugin/package.json',
|
|
51
|
+
{
|
|
52
|
+
assert: { type: 'json' },
|
|
53
|
+
}
|
|
54
|
+
).then(({ default: packageJSON }) => packageJSON);
|
|
55
|
+
|
|
56
|
+
const inputs = actionArgs.filter(
|
|
57
|
+
(arg) => typeof arg === 'string'
|
|
58
|
+
) as string[];
|
|
59
|
+
const args = actionArgs.find(
|
|
60
|
+
(arg) => arg && typeof arg === 'object'
|
|
61
|
+
) as Record<string, any>;
|
|
62
|
+
|
|
63
|
+
if (!args) throw new Error('Arguments object not found');
|
|
64
|
+
|
|
65
|
+
if (args.version) {
|
|
66
|
+
console.info(`v${packageVersion}`);
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.info(`✨ ${c.bold(`OpenAPI Qraft ${packageVersion}`)}`);
|
|
71
|
+
|
|
72
|
+
const spinner = ora('Starting the Qraft ⛏︎').start();
|
|
73
|
+
|
|
74
|
+
await callback(
|
|
75
|
+
{
|
|
76
|
+
inputs,
|
|
77
|
+
args,
|
|
78
|
+
spinner,
|
|
79
|
+
services: await this.#getServices(inputs[0], args),
|
|
80
|
+
output: {
|
|
81
|
+
dir: normalizeOutputDirPath(args.outputDir),
|
|
82
|
+
clean: args.clean,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
async function resolveGeneratorFiles(fileItems) {
|
|
86
|
+
try {
|
|
87
|
+
await writeGeneratorFiles({
|
|
88
|
+
fileItems,
|
|
89
|
+
spinner,
|
|
90
|
+
});
|
|
91
|
+
spinner.succeed(c.green('Qraft has been finished'));
|
|
92
|
+
} catch (error) {
|
|
93
|
+
spinner.fail(c.red('Error occurred during generation'));
|
|
94
|
+
|
|
95
|
+
if (error instanceof Error) {
|
|
96
|
+
console.error(c.red(error.message), c.red(error.stack ?? ''));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.error(error);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async #getServices(input: unknown, args: Record<string, any>) {
|
|
108
|
+
const cwd = `${process.cwd()}/`;
|
|
109
|
+
|
|
110
|
+
const source =
|
|
111
|
+
input && typeof input === 'string'
|
|
112
|
+
? new URL(input, pathToFileURL(cwd))
|
|
113
|
+
: process.stdin;
|
|
114
|
+
|
|
115
|
+
if (source === process.stdin && source.isTTY) {
|
|
116
|
+
console.error(
|
|
117
|
+
c.red(
|
|
118
|
+
'Input file not found or stdin is empty. Please specify `--input` option or pipe OpenAPI Schema to stdin.'
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return await getDocumentServices({
|
|
126
|
+
output: {
|
|
127
|
+
postfixServices: args.postfixServices,
|
|
128
|
+
serviceNameBase: args.serviceNameBase,
|
|
129
|
+
},
|
|
130
|
+
servicesGlob: parseServicesFilterOption(args.filterServices),
|
|
131
|
+
source,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Normalize an output directory path by adding trailing slash
|
|
138
|
+
*/
|
|
139
|
+
function normalizeOutputDirPath(outputDir: string): URL {
|
|
140
|
+
return pathToFileURL(
|
|
141
|
+
outputDir.endsWith(sep) ? outputDir : `${outputDir}${sep}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Parse services filter option to array
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* parseServicesFilterOption('/user/**,/post/**')
|
|
150
|
+
* ```
|
|
151
|
+
* @param endpointGlobs
|
|
152
|
+
*/
|
|
153
|
+
function parseServicesFilterOption(
|
|
154
|
+
endpointGlobs: string
|
|
155
|
+
): string[] | undefined {
|
|
156
|
+
return endpointGlobs
|
|
157
|
+
? endpointGlobs
|
|
158
|
+
.split(',')
|
|
159
|
+
.filter((item: unknown): item is string =>
|
|
160
|
+
Boolean(typeof item === 'string' && item.trim())
|
|
161
|
+
)
|
|
162
|
+
: undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export type QraftCommandActionCallback = (
|
|
166
|
+
options: {
|
|
167
|
+
/**
|
|
168
|
+
* Command inputs, e.g. `bin <input> [options]` where `<input>` is the `inputs` item
|
|
169
|
+
*/
|
|
170
|
+
inputs: string[];
|
|
171
|
+
/**
|
|
172
|
+
* Command arguments, e.g. `bin <input> [options]` where `[options]` is the `args`
|
|
173
|
+
*/
|
|
174
|
+
args: Record<string, any>;
|
|
175
|
+
/**
|
|
176
|
+
* Resolve the generator files
|
|
177
|
+
*/
|
|
178
|
+
/**
|
|
179
|
+
* Spinner instance
|
|
180
|
+
*/
|
|
181
|
+
spinner: Ora;
|
|
182
|
+
/**
|
|
183
|
+
* OpenAPI services
|
|
184
|
+
*/
|
|
185
|
+
services: Service[];
|
|
186
|
+
/**
|
|
187
|
+
* Output options
|
|
188
|
+
*/
|
|
189
|
+
output: OutputOptions;
|
|
190
|
+
},
|
|
191
|
+
resolve: (files: GeneratorFile[]) => Promise<void>
|
|
192
|
+
) => void | Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type OpenAPISchemaType = {
|
|
2
|
+
paths: {
|
|
3
|
+
[path: string]: {
|
|
4
|
+
[method: string]: {
|
|
5
|
+
tags?: string[];
|
|
6
|
+
description?: string;
|
|
7
|
+
summary?: string;
|
|
8
|
+
operationId?: string;
|
|
9
|
+
parameters?: Record<string, any>;
|
|
10
|
+
deprecated?: boolean;
|
|
11
|
+
requestBody?: {
|
|
12
|
+
content: {
|
|
13
|
+
[contentType: string]: {
|
|
14
|
+
schema: any;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
responses: {
|
|
19
|
+
[statusCode in number | 'default']: {
|
|
20
|
+
description: string;
|
|
21
|
+
content: {
|
|
22
|
+
[contentType: string]: {
|
|
23
|
+
schema: any;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const BASIC_MEDIA_TYPES = [
|
|
2
|
+
'application/json-patch+json',
|
|
3
|
+
'application/json',
|
|
4
|
+
'application/x-www-form-urlencoded',
|
|
5
|
+
'text/json',
|
|
6
|
+
'text/plain',
|
|
7
|
+
'multipart/form-data',
|
|
8
|
+
'multipart/mixed',
|
|
9
|
+
'multipart/related',
|
|
10
|
+
'multipart/batch',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export const getContentMediaType = (content: {
|
|
14
|
+
[contentType: string]: { schema: unknown };
|
|
15
|
+
}) => {
|
|
16
|
+
const basicMediaTypeWithSchema = Object.keys(content)
|
|
17
|
+
.filter((mediaType) => {
|
|
18
|
+
const cleanMediaType = mediaType.split(';')[0].trim();
|
|
19
|
+
return BASIC_MEDIA_TYPES.includes(cleanMediaType);
|
|
20
|
+
})
|
|
21
|
+
.find((mediaType) => content[mediaType]?.schema);
|
|
22
|
+
|
|
23
|
+
if (basicMediaTypeWithSchema) return basicMediaTypeWithSchema;
|
|
24
|
+
|
|
25
|
+
return Object.keys(content).find((mediaType) => content[mediaType]?.schema);
|
|
26
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { URL } from 'node:url';
|
|
3
|
+
import type { OpenAPI3 } from 'openapi-typescript';
|
|
4
|
+
|
|
5
|
+
import { getServices, ServiceOutputOptions } from './getServices.js';
|
|
6
|
+
import { readSchema } from './readSchema.js';
|
|
7
|
+
|
|
8
|
+
export const getDocumentServices = async ({
|
|
9
|
+
source,
|
|
10
|
+
servicesGlob,
|
|
11
|
+
output,
|
|
12
|
+
}: {
|
|
13
|
+
source: string | URL | OpenAPI3 | Readable | Buffer;
|
|
14
|
+
servicesGlob: string[] | undefined;
|
|
15
|
+
output: ServiceOutputOptions;
|
|
16
|
+
}) => {
|
|
17
|
+
const schema = await readSchema(source);
|
|
18
|
+
return getServices(schema, output, servicesGlob);
|
|
19
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import camelCase from 'camelcase';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert the input value to a correct operation (method) classname.
|
|
5
|
+
* This will use the operation ID - if available - and otherwise fallback
|
|
6
|
+
* on a generated name from the URL
|
|
7
|
+
*/
|
|
8
|
+
export const getOperationName = (
|
|
9
|
+
url: string,
|
|
10
|
+
method: string,
|
|
11
|
+
operationId?: string
|
|
12
|
+
): string => {
|
|
13
|
+
if (operationId) {
|
|
14
|
+
return camelCase(
|
|
15
|
+
operationId
|
|
16
|
+
.replace(/^[^a-zA-Z]+/g, '')
|
|
17
|
+
.replace(/[^\w-]+/g, '-')
|
|
18
|
+
.trim()
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const urlWithoutPlaceholders = url
|
|
23
|
+
.replace(/[^/]*?{api-version}.*?\//g, '')
|
|
24
|
+
.replace(/{(.*?)}/g, '')
|
|
25
|
+
.replace(/\//g, '-');
|
|
26
|
+
|
|
27
|
+
return camelCase(`${method}-${urlWithoutPlaceholders}`);
|
|
28
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import camelCase from 'camelcase';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert the input value to the correct service name. This converts
|
|
5
|
+
* the input string to PascalCase.
|
|
6
|
+
*/
|
|
7
|
+
export const getServiceName = (value: string): string => {
|
|
8
|
+
const clean = value
|
|
9
|
+
.replace(/^[^a-zA-Z]+/g, '')
|
|
10
|
+
.replace(/[^\w-]+/g, '-')
|
|
11
|
+
.trim();
|
|
12
|
+
|
|
13
|
+
return camelCase(clean, { pascalCase: true });
|
|
14
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getServiceName } from './getServiceName.js';
|
|
2
|
+
|
|
3
|
+
export type ServiceBaseNameByEndpointOption = `endpoint[${number}]`;
|
|
4
|
+
|
|
5
|
+
export const getServiceNamesByOperationEndpoint = (
|
|
6
|
+
endpoint: string,
|
|
7
|
+
format: ServiceBaseNameByEndpointOption,
|
|
8
|
+
fallbackBaseName: string
|
|
9
|
+
): [string] => {
|
|
10
|
+
return [
|
|
11
|
+
getServiceName(
|
|
12
|
+
getServiceBaseNameByOperationEndpoint(endpoint, format) ??
|
|
13
|
+
fallbackBaseName
|
|
14
|
+
),
|
|
15
|
+
];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getServiceBaseNameByOperationEndpoint = (
|
|
19
|
+
endpoint: string,
|
|
20
|
+
format: ServiceBaseNameByEndpointOption
|
|
21
|
+
): string | undefined => {
|
|
22
|
+
const endpointParts = endpoint.split('/').filter(Boolean);
|
|
23
|
+
|
|
24
|
+
for (
|
|
25
|
+
let endpointIndex = getEndpointPartIndex(format);
|
|
26
|
+
endpointIndex >= 0;
|
|
27
|
+
endpointIndex--
|
|
28
|
+
) {
|
|
29
|
+
const endpointPart = endpointParts[endpointIndex];
|
|
30
|
+
if (endpointPart) return endpointPart;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const getEndpointPartIndex = (
|
|
35
|
+
endpointOption: ServiceBaseNameByEndpointOption
|
|
36
|
+
) => {
|
|
37
|
+
const startBracketString = 'endpoint[';
|
|
38
|
+
|
|
39
|
+
const endpointIndexStart = endpointOption.indexOf(startBracketString);
|
|
40
|
+
|
|
41
|
+
if (endpointIndexStart === -1)
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Expected endpoint to start with '${startBracketString}' but got: '${endpointOption}'`
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const endBracketString = ']';
|
|
47
|
+
const endpointIndexEnd = endpointOption.indexOf(
|
|
48
|
+
endBracketString,
|
|
49
|
+
endpointIndexStart
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (endpointIndexEnd === -1)
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Expected endpoint to end with '${endBracketString}' but got: '${endpointOption}'`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const endpointIndex = parseInt(
|
|
58
|
+
endpointOption.slice(
|
|
59
|
+
endpointIndexStart + startBracketString.length,
|
|
60
|
+
endpointIndexEnd
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (!isFinite(endpointIndex))
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Expected endpoint index to be a number but got: '${endpointOption}'`
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return endpointIndex;
|
|
70
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getServiceName } from './getServiceName.js';
|
|
2
|
+
|
|
3
|
+
export const getServiceNamesByOperationTags = (
|
|
4
|
+
tags: (string | number | null | undefined)[] | undefined,
|
|
5
|
+
fallbackBaseName: string
|
|
6
|
+
): string[] => {
|
|
7
|
+
const filteredTags = tags
|
|
8
|
+
?.filter((tag): tag is string => typeof tag === 'string')
|
|
9
|
+
.map(getServiceName)
|
|
10
|
+
.filter(Boolean);
|
|
11
|
+
|
|
12
|
+
return filteredTags?.length
|
|
13
|
+
? filteredTags
|
|
14
|
+
: [getServiceName(fallbackBaseName)];
|
|
15
|
+
};
|