@travetto/openapi 3.0.0-rc.2 → 3.0.0-rc.20
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/README.md +47 -27
- package/{index.ts → __index__.ts} +0 -0
- package/package.json +13 -12
- package/src/config.ts +36 -18
- package/src/service.ts +5 -2
- package/src/spec-generate.ts +5 -6
- package/support/bin/generate.ts +11 -0
- package/{bin/cli-openapi_client.ts → support/cli.openapi_client.ts} +38 -29
- package/support/cli.openapi_spec.ts +37 -0
- package/{resources → support/resources}/presets.json +0 -0
- package/bin/cli-openapi_spec.ts +0 -33
- package/bin/generate.ts +0 -12
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- This file was generated by @travetto/doc and should not be modified directly -->
|
|
2
|
-
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/openapi/
|
|
2
|
+
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/openapi/DOC.ts and execute "npx trv doc" to rebuild -->
|
|
3
3
|
# OpenAPI Specification
|
|
4
4
|
## OpenAPI integration support for the travetto framework
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ npm install @travetto/openapi
|
|
|
10
10
|
|
|
11
11
|
In the [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") module, the controllers and endpoints can be described via decorators, comments, or typings. This only provides the general metadata internally. This is not sufficient to generate a usable API doc, and so this module exists to bridge that gap.
|
|
12
12
|
|
|
13
|
-
The module is provides an [OpenAPI](https://github.com/OAI/OpenAPI-Specification) v3.x representation of the API metadata provided via the [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") and [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.
|
|
13
|
+
The module is provides an [OpenAPI](https://github.com/OAI/OpenAPI-Specification) v3.x representation of the API metadata provided via the [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") and [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") modules.
|
|
14
14
|
|
|
15
15
|
## Configuration
|
|
16
16
|
By installing the dependency, the [OpenAPI](https://github.com/OAI/OpenAPI-Specification) endpoint is automatically generated and exposed at the root of the application as `/openapi.yml` or `/openapi.json` (by default).
|
|
@@ -19,31 +19,45 @@ All of the high level configurations can be found in the following structure:
|
|
|
19
19
|
|
|
20
20
|
**Code: Config: OpenAPI Configuration**
|
|
21
21
|
```typescript
|
|
22
|
-
import
|
|
23
|
-
import * as fs from 'fs/promises';
|
|
24
|
-
import { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts/src/model/OpenApi';
|
|
22
|
+
import type { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts/src/model/OpenApi';
|
|
25
23
|
|
|
26
|
-
import { Config } from '@travetto/config';
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
24
|
+
import { Config, EnvVar } from '@travetto/config';
|
|
25
|
+
import { path, RootIndex } from '@travetto/manifest';
|
|
26
|
+
import { GlobalEnv } from '@travetto/base';
|
|
27
|
+
import { Required } from '@travetto/schema';
|
|
29
28
|
|
|
30
29
|
/**
|
|
31
30
|
* API Information, infers as much as possible from the package.json
|
|
32
31
|
*/
|
|
33
|
-
@Config('api.info'
|
|
32
|
+
@Config('api.info')
|
|
34
33
|
export class ApiInfoConfig {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
@Required(false)
|
|
35
|
+
contact: ContactObject;
|
|
36
|
+
@Required(false)
|
|
37
|
+
description?: string;
|
|
38
|
+
@Required(false)
|
|
39
|
+
license: LicenseObject;
|
|
40
|
+
@Required(false)
|
|
38
41
|
termsOfService?: string;
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
@Required(false)
|
|
43
|
+
title: string;
|
|
44
|
+
@Required(false)
|
|
45
|
+
version: string;
|
|
46
|
+
|
|
47
|
+
postConstruct(): void {
|
|
48
|
+
const info = RootIndex.mainPackage;
|
|
49
|
+
this.contact ??= info.author ?? {};
|
|
50
|
+
this.description ??= info.description;
|
|
51
|
+
this.license ??= { name: info.license! };
|
|
52
|
+
this.title ??= info.name;
|
|
53
|
+
this.version ??= info.version;
|
|
54
|
+
}
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
/**
|
|
44
58
|
* The API host, infers from rest host configuration
|
|
45
59
|
*/
|
|
46
|
-
@Config('api.host'
|
|
60
|
+
@Config('api.host')
|
|
47
61
|
export class ApiHostConfig {
|
|
48
62
|
/**
|
|
49
63
|
* List of servers
|
|
@@ -58,16 +72,18 @@ export class ApiHostConfig {
|
|
|
58
72
|
/**
|
|
59
73
|
* The spec file configuration
|
|
60
74
|
*/
|
|
61
|
-
@Config('api.spec'
|
|
75
|
+
@Config('api.spec')
|
|
62
76
|
export class ApiSpecConfig {
|
|
63
77
|
/**
|
|
64
78
|
* Where to output file to
|
|
65
79
|
*/
|
|
80
|
+
@EnvVar('TRV_OPENAPI_OUTPUT')
|
|
66
81
|
output: string = 'openapi.yml';
|
|
67
82
|
/**
|
|
68
83
|
* Should file be generated at runtime
|
|
69
84
|
*/
|
|
70
|
-
|
|
85
|
+
@EnvVar('TRV_OPENAPI_PERSIST')
|
|
86
|
+
persist?: boolean;
|
|
71
87
|
/**
|
|
72
88
|
* Skip emitting all routes
|
|
73
89
|
*/
|
|
@@ -78,15 +94,16 @@ export class ApiSpecConfig {
|
|
|
78
94
|
exposeAllSchemas: boolean = false;
|
|
79
95
|
|
|
80
96
|
async postConstruct(): Promise<void> {
|
|
81
|
-
this.output =
|
|
97
|
+
this.output = path.toPosix(this.output);
|
|
82
98
|
if (!this.output || this.output === '-') {
|
|
83
99
|
this.persist = false;
|
|
100
|
+
} else {
|
|
101
|
+
this.persist ??= GlobalEnv.dynamic;
|
|
84
102
|
}
|
|
85
103
|
if (this.persist) {
|
|
86
104
|
if (!/[.](json|ya?ml)$/.test(this.output)) { // Assume a folder
|
|
87
|
-
this.output =
|
|
105
|
+
this.output = path.resolve(this.output, 'openapi.yml');
|
|
88
106
|
}
|
|
89
|
-
await fs.mkdir(path.dirname(this.output), { recursive: true });
|
|
90
107
|
}
|
|
91
108
|
}
|
|
92
109
|
}
|
|
@@ -127,18 +144,21 @@ Usage: openapi:client [options] [format-or-preset]
|
|
|
127
144
|
Options:
|
|
128
145
|
-x, --extended-help Show Extended Help
|
|
129
146
|
-a, --additional-properties <additional-properties> Additional Properties (default: [])
|
|
130
|
-
-i, --input <input> Input file (default:
|
|
131
|
-
|
|
132
|
-
-
|
|
147
|
+
-i, --input <input> Input file (default:
|
|
148
|
+
"@travetto/openapi/openapi.yml")
|
|
149
|
+
-o, --output <output> Output folder (default:
|
|
150
|
+
"@travetto/openapi/api-client")
|
|
151
|
+
-d, --docker-image <docker-image> Docker Image to use (default:
|
|
152
|
+
"arcsine/openapi-generator:latest")
|
|
133
153
|
-w, --watch Watch for file changes
|
|
134
154
|
-h, --help display help for command
|
|
135
155
|
|
|
136
156
|
Available Presets
|
|
137
157
|
----------------------------------
|
|
138
|
-
* @
|
|
139
|
-
* @
|
|
140
|
-
* @
|
|
141
|
-
* @
|
|
158
|
+
* @travetto/angular10 -- typescript-angular supportsES6=true,ngVersion=10.0
|
|
159
|
+
* @travetto/angular11 -- typescript-angular supportsES6=true,ngVersion=11.0
|
|
160
|
+
* @travetto/angular12 -- typescript-angular supportsES6=true,ngVersion=11.0
|
|
161
|
+
* @travetto/fetch -- typescript-fetch
|
|
142
162
|
```
|
|
143
163
|
|
|
144
164
|
This tool relies upon a custom build of [OpenAPI client generation tools](https://github.com/OpenAPITools/openapi-generator), which supports watching. This allows for fast responsive client generation as the shape of the API changes.
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/openapi",
|
|
3
|
-
"
|
|
4
|
-
"version": "3.0.0-rc.2",
|
|
3
|
+
"version": "3.0.0-rc.20",
|
|
5
4
|
"description": "OpenAPI integration support for the travetto framework",
|
|
6
5
|
"keywords": [
|
|
7
6
|
"rest",
|
|
@@ -17,31 +16,33 @@
|
|
|
17
16
|
"name": "Travetto Framework"
|
|
18
17
|
},
|
|
19
18
|
"files": [
|
|
20
|
-
"
|
|
19
|
+
"__index__.ts",
|
|
21
20
|
"src",
|
|
22
|
-
"
|
|
23
|
-
"resources"
|
|
21
|
+
"support"
|
|
24
22
|
],
|
|
25
|
-
"main": "
|
|
23
|
+
"main": "__index__.ts",
|
|
26
24
|
"repository": {
|
|
27
25
|
"url": "https://github.com/travetto/travetto.git",
|
|
28
26
|
"directory": "module/openapi"
|
|
29
27
|
},
|
|
30
28
|
"dependencies": {
|
|
31
|
-
"@travetto/config": "^3.0.0-rc.
|
|
32
|
-
"@travetto/rest": "^3.0.0-rc.
|
|
33
|
-
"@travetto/schema": "^3.0.0-rc.
|
|
34
|
-
"@travetto/yaml": "^3.0.0-rc.
|
|
35
|
-
"openapi3-ts": "^
|
|
29
|
+
"@travetto/config": "^3.0.0-rc.19",
|
|
30
|
+
"@travetto/rest": "^3.0.0-rc.20",
|
|
31
|
+
"@travetto/schema": "^3.0.0-rc.19",
|
|
32
|
+
"@travetto/yaml": "^3.0.0-rc.15",
|
|
33
|
+
"openapi3-ts": "^3.1.2"
|
|
36
34
|
},
|
|
37
35
|
"peerDependencies": {
|
|
38
|
-
"@travetto/cli": "^3.0.0-rc.
|
|
36
|
+
"@travetto/cli": "^3.0.0-rc.16"
|
|
39
37
|
},
|
|
40
38
|
"peerDependenciesMeta": {
|
|
41
39
|
"@travetto/cli": {
|
|
42
40
|
"optional": true
|
|
43
41
|
}
|
|
44
42
|
},
|
|
43
|
+
"travetto": {
|
|
44
|
+
"displayName": "OpenAPI Specification"
|
|
45
|
+
},
|
|
45
46
|
"publishConfig": {
|
|
46
47
|
"access": "public"
|
|
47
48
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,28 +1,43 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import {
|
|
1
|
+
import type { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts/src/model/OpenApi';
|
|
2
|
+
|
|
3
|
+
import { Config, EnvVar } from '@travetto/config';
|
|
4
|
+
import { path, RootIndex } from '@travetto/manifest';
|
|
5
|
+
import { GlobalEnv } from '@travetto/base';
|
|
6
|
+
import { Required } from '@travetto/schema';
|
|
4
7
|
|
|
5
|
-
import { Config } from '@travetto/config';
|
|
6
|
-
import { PathUtil, EnvUtil } from '@travetto/boot';
|
|
7
|
-
import { AppManifest } from '@travetto/base';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* API Information, infers as much as possible from the package.json
|
|
11
11
|
*/
|
|
12
|
-
@Config('api.info'
|
|
12
|
+
@Config('api.info')
|
|
13
13
|
export class ApiInfoConfig {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
@Required(false)
|
|
15
|
+
contact: ContactObject;
|
|
16
|
+
@Required(false)
|
|
17
|
+
description?: string;
|
|
18
|
+
@Required(false)
|
|
19
|
+
license: LicenseObject;
|
|
20
|
+
@Required(false)
|
|
17
21
|
termsOfService?: string;
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
@Required(false)
|
|
23
|
+
title: string;
|
|
24
|
+
@Required(false)
|
|
25
|
+
version: string;
|
|
26
|
+
|
|
27
|
+
postConstruct(): void {
|
|
28
|
+
const info = RootIndex.mainPackage;
|
|
29
|
+
this.contact ??= info.author ?? {};
|
|
30
|
+
this.description ??= info.description;
|
|
31
|
+
this.license ??= { name: info.license! };
|
|
32
|
+
this.title ??= info.name;
|
|
33
|
+
this.version ??= info.version;
|
|
34
|
+
}
|
|
20
35
|
}
|
|
21
36
|
|
|
22
37
|
/**
|
|
23
38
|
* The API host, infers from rest host configuration
|
|
24
39
|
*/
|
|
25
|
-
@Config('api.host'
|
|
40
|
+
@Config('api.host')
|
|
26
41
|
export class ApiHostConfig {
|
|
27
42
|
/**
|
|
28
43
|
* List of servers
|
|
@@ -37,16 +52,18 @@ export class ApiHostConfig {
|
|
|
37
52
|
/**
|
|
38
53
|
* The spec file configuration
|
|
39
54
|
*/
|
|
40
|
-
@Config('api.spec'
|
|
55
|
+
@Config('api.spec')
|
|
41
56
|
export class ApiSpecConfig {
|
|
42
57
|
/**
|
|
43
58
|
* Where to output file to
|
|
44
59
|
*/
|
|
60
|
+
@EnvVar('TRV_OPENAPI_OUTPUT')
|
|
45
61
|
output: string = 'openapi.yml';
|
|
46
62
|
/**
|
|
47
63
|
* Should file be generated at runtime
|
|
48
64
|
*/
|
|
49
|
-
|
|
65
|
+
@EnvVar('TRV_OPENAPI_PERSIST')
|
|
66
|
+
persist?: boolean;
|
|
50
67
|
/**
|
|
51
68
|
* Skip emitting all routes
|
|
52
69
|
*/
|
|
@@ -57,15 +74,16 @@ export class ApiSpecConfig {
|
|
|
57
74
|
exposeAllSchemas: boolean = false;
|
|
58
75
|
|
|
59
76
|
async postConstruct(): Promise<void> {
|
|
60
|
-
this.output =
|
|
77
|
+
this.output = path.toPosix(this.output);
|
|
61
78
|
if (!this.output || this.output === '-') {
|
|
62
79
|
this.persist = false;
|
|
80
|
+
} else {
|
|
81
|
+
this.persist ??= GlobalEnv.dynamic;
|
|
63
82
|
}
|
|
64
83
|
if (this.persist) {
|
|
65
84
|
if (!/[.](json|ya?ml)$/.test(this.output)) { // Assume a folder
|
|
66
|
-
this.output =
|
|
85
|
+
this.output = path.resolve(this.output, 'openapi.yml');
|
|
67
86
|
}
|
|
68
|
-
await fs.mkdir(path.dirname(this.output), { recursive: true });
|
|
69
87
|
}
|
|
70
88
|
}
|
|
71
89
|
}
|
package/src/service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { OpenAPIObject } from 'openapi3-ts';
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import type { OpenAPIObject } from 'openapi3-ts';
|
|
3
3
|
|
|
4
|
+
import { path } from '@travetto/manifest';
|
|
4
5
|
import { Injectable, Inject } from '@travetto/di';
|
|
5
6
|
import { ControllerRegistry, RestConfig } from '@travetto/rest';
|
|
6
7
|
import { SchemaRegistry } from '@travetto/schema';
|
|
@@ -78,6 +79,8 @@ export class OpenApiService {
|
|
|
78
79
|
JSON.stringify(this.spec, undefined, 2) :
|
|
79
80
|
YamlUtil.serialize(this.spec);
|
|
80
81
|
|
|
82
|
+
// TODO: Should use file abstraction
|
|
83
|
+
await fs.mkdir(path.dirname(this.apiSpecConfig.output), { recursive: true });
|
|
81
84
|
await fs.writeFile(this.apiSpecConfig.output, output);
|
|
82
85
|
} catch (err) {
|
|
83
86
|
console.error('Unable to persist openapi spec', err);
|
package/src/spec-generate.ts
CHANGED
|
@@ -40,7 +40,7 @@ export class SpecGenerator {
|
|
|
40
40
|
* @param cls
|
|
41
41
|
*/
|
|
42
42
|
#getTypeId(cls: Class): string {
|
|
43
|
-
return cls.name?.replace('
|
|
43
|
+
return cls.name?.replace('Ⲑsyn', '');
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -69,7 +69,7 @@ export class SpecGenerator {
|
|
|
69
69
|
const viewConf = SchemaRegistry.has(field.type) && SchemaRegistry.getViewSchema(field.type, field.view);
|
|
70
70
|
const schemaConf = viewConf && viewConf.schema;
|
|
71
71
|
if (!schemaConf) {
|
|
72
|
-
throw new Error(`Unknown class, not registered as a schema: ${field.type
|
|
72
|
+
throw new Error(`Unknown class, not registered as a schema: ${field.type.Ⲑid}`);
|
|
73
73
|
}
|
|
74
74
|
const params: ParameterObject[] = [];
|
|
75
75
|
for (const sub of Object.values(schemaConf)) {
|
|
@@ -85,8 +85,7 @@ export class SpecGenerator {
|
|
|
85
85
|
...this.#getType(sub)
|
|
86
86
|
} : this.#getType(sub),
|
|
87
87
|
required: !!(rootField?.required?.active && sub.required?.active),
|
|
88
|
-
in: location
|
|
89
|
-
extract: undefined
|
|
88
|
+
in: location
|
|
90
89
|
});
|
|
91
90
|
}
|
|
92
91
|
}
|
|
@@ -245,7 +244,7 @@ export class SpecGenerator {
|
|
|
245
244
|
} else if (body.type === Readable || body.type === Buffer) {
|
|
246
245
|
return {
|
|
247
246
|
content: {
|
|
248
|
-
[mime ?? 'application/octet-stream']: { type: 'string', format: 'binary' }
|
|
247
|
+
[mime ?? 'application/octet-stream']: { schema: { type: 'string', format: 'binary' } }
|
|
249
248
|
},
|
|
250
249
|
description: ''
|
|
251
250
|
};
|
|
@@ -379,7 +378,7 @@ export class SpecGenerator {
|
|
|
379
378
|
|
|
380
379
|
if (!config.skipRoutes) {
|
|
381
380
|
for (const cls of ControllerRegistry.getClasses()) {
|
|
382
|
-
if (cls
|
|
381
|
+
if (cls.Ⲑid !== OpenApiController.Ⲑid) {
|
|
383
382
|
this.processController(cls);
|
|
384
383
|
}
|
|
385
384
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DependencyRegistry } from '@travetto/di';
|
|
2
|
+
import { RootRegistry } from '@travetto/registry';
|
|
3
|
+
|
|
4
|
+
import { OpenApiService } from '../../src/service';
|
|
5
|
+
|
|
6
|
+
export async function main(): Promise<unknown> {
|
|
7
|
+
await RootRegistry.init();
|
|
8
|
+
|
|
9
|
+
const instance = await DependencyRegistry.getInstance(OpenApiService);
|
|
10
|
+
return instance.spec;
|
|
11
|
+
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
3
|
+
import cp from 'child_process';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
const presets: Record<string, [string, object] | [string]> = JSON.parse(readFileSync(PathUtil.resolveUnix(__dirname, '..', 'resources', 'presets.json'), 'utf8'));
|
|
5
|
+
import { path, RootIndex } from '@travetto/manifest';
|
|
6
|
+
import { ExecUtil, FileResourceProvider } from '@travetto/base';
|
|
7
|
+
import { CliCommand, cliTpl, OptionConfig, ListOptionConfig } from '@travetto/cli';
|
|
10
8
|
|
|
11
9
|
type Options = {
|
|
12
10
|
extendedHelp: OptionConfig<boolean>;
|
|
@@ -20,26 +18,36 @@ type Options = {
|
|
|
20
18
|
* CLI for generating the cli client
|
|
21
19
|
*/
|
|
22
20
|
export class OpenApiClientCommand extends CliCommand<Options> {
|
|
21
|
+
#presets: Record<string, [string, object] | [string]>;
|
|
23
22
|
name = 'openapi:client';
|
|
23
|
+
#resources = new FileResourceProvider(['@travetto/openapi#support/resources']);
|
|
24
|
+
|
|
25
|
+
async getPresets(): Promise<Record<string, [string, object] | [string]>> {
|
|
26
|
+
if (!this.#presets) {
|
|
27
|
+
const text = await this.#resources.read('presets.json');
|
|
28
|
+
this.#presets = JSON.parse(text);
|
|
29
|
+
}
|
|
30
|
+
return this.#presets;
|
|
31
|
+
}
|
|
24
32
|
|
|
25
33
|
presetMap(prop?: object): string {
|
|
26
34
|
return !prop || Object.keys(prop).length === 0 ? '' : Object.entries(prop).map(([k, v]) => `${k}=${v}`).join(',');
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
getListOfFormats(): string[] {
|
|
30
|
-
const
|
|
31
|
-
|
|
38
|
+
const formatCache = path.resolve(RootIndex.manifest.workspacePath, RootIndex.manifest.outputFolder, 'trv-openapi-formats.json');
|
|
39
|
+
if (!existsSync(formatCache)) {
|
|
40
|
+
const stdout = cp.execSync(`docker run --rm ${this.cmd.dockerImage} list`, { stdio: ['pipe', 'pipe'], encoding: 'utf8' }).trim();
|
|
32
41
|
const lines = stdout
|
|
33
42
|
.split('DOCUMENTATION')[0]
|
|
34
43
|
.trim()
|
|
35
44
|
.split(/\n/g)
|
|
36
45
|
.filter(x => /^\s+-/.test(x) && !/\((beta|experimental)\)/.test(x))
|
|
37
46
|
.map(x => x.replace(/^\s+-\s+/, '').trim());
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const list: string[] = JSON.parse(json);
|
|
47
|
+
|
|
48
|
+
writeFileSync(formatCache, JSON.stringify([...lines.sort(),]));
|
|
49
|
+
}
|
|
50
|
+
const list: string[] = JSON.parse(readFileSync(formatCache, 'utf8'));
|
|
43
51
|
return list;
|
|
44
52
|
}
|
|
45
53
|
|
|
@@ -47,40 +55,41 @@ export class OpenApiClientCommand extends CliCommand<Options> {
|
|
|
47
55
|
return {
|
|
48
56
|
extendedHelp: this.boolOption({ name: 'extended-help', short: 'x', desc: 'Show Extended Help' }),
|
|
49
57
|
props: this.listOption({ name: 'additional-properties', short: 'a', desc: 'Additional Properties' }),
|
|
50
|
-
input: this.option({ desc: 'Input file', def: './openapi.yml', combine: v =>
|
|
51
|
-
output: this.option({ desc: 'Output folder', def: './api-client', combine: v =>
|
|
58
|
+
input: this.option({ desc: 'Input file', def: './openapi.yml', combine: v => path.resolve(v), completion: true }),
|
|
59
|
+
output: this.option({ desc: 'Output folder', def: './api-client', combine: v => path.resolve(v), completion: true }),
|
|
52
60
|
dockerImage: this.option({ desc: 'Docker Image to use', def: 'arcsine/openapi-generator:latest' }),
|
|
53
61
|
watch: this.boolOption({ desc: 'Watch for file changes' })
|
|
54
62
|
};
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
help(): string {
|
|
65
|
+
async help(): Promise<string> {
|
|
66
|
+
const presets = await this.getPresets();
|
|
58
67
|
const presetLen = Math.max(...Object.keys(presets).map(x => x.length));
|
|
59
68
|
const presetEntries = Object
|
|
60
69
|
.entries(presets)
|
|
61
70
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
62
|
-
.map(([k, [cmd, v]]) => [`@
|
|
71
|
+
.map(([k, [cmd, v]]) => [`@travetto/${k}`.padEnd(presetLen + 5), [cmd, this.presetMap(v)]] as const);
|
|
63
72
|
|
|
64
|
-
const presetText =
|
|
73
|
+
const presetText = cliTpl`
|
|
65
74
|
${{ subtitle: 'Available Presets' }}
|
|
66
75
|
----------------------------------
|
|
67
|
-
${presetEntries.map(([k, [cmd, param]]) =>
|
|
76
|
+
${presetEntries.map(([k, [cmd, param]]) => cliTpl`* ${{ input: k }} -- ${{ identifier: cmd }} ${{ param }}`).join('\n')}`;
|
|
68
77
|
|
|
69
|
-
const formatText =
|
|
78
|
+
const formatText = cliTpl`
|
|
70
79
|
${{ subtitle: 'Available Formats' }}
|
|
71
80
|
----------------------------------
|
|
72
|
-
${this.getListOfFormats().map(x =>
|
|
81
|
+
${this.getListOfFormats().map(x => cliTpl`* ${{ input: x }}`).join('\n')} `;
|
|
73
82
|
|
|
74
83
|
return this.cmd.extendedHelp ? `${presetText}\n${formatText}` : presetText;
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
getArgs(): string {
|
|
78
|
-
return '
|
|
87
|
+
return '<format-or-preset>';
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
async action(format: string): Promise<void> {
|
|
82
91
|
if (!format) {
|
|
83
|
-
this.showHelp(
|
|
92
|
+
return this.showHelp('Format is required');
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
// Ensure its there
|
|
@@ -88,9 +97,9 @@ ${this.getListOfFormats().map(x => color`* ${{ input: x }}`).join('\n')} `;
|
|
|
88
97
|
|
|
89
98
|
let propMap = Object.fromEntries(this.cmd.props?.map(p => p.split('=')) ?? []);
|
|
90
99
|
|
|
91
|
-
if (format.startsWith('@
|
|
92
|
-
const key = format.split('@
|
|
93
|
-
const [fmt, props] =
|
|
100
|
+
if (format.startsWith('@travetto/')) {
|
|
101
|
+
const key = format.split('@travetto/')[1];
|
|
102
|
+
const [fmt, props] = (await this.getPresets())[key];
|
|
94
103
|
format = fmt;
|
|
95
104
|
propMap = { ...props, ...propMap };
|
|
96
105
|
}
|
|
@@ -116,6 +125,6 @@ ${this.getListOfFormats().map(x => color`* ${{ input: x }}`).join('\n')} `;
|
|
|
116
125
|
];
|
|
117
126
|
|
|
118
127
|
const { result } = ExecUtil.spawn('docker', args, { stdio: [0, 1, 2] });
|
|
119
|
-
await result.catch(err =>
|
|
128
|
+
await result.catch(err => this.exit(1));
|
|
120
129
|
}
|
|
121
130
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CliCommand, OptionConfig } from '@travetto/cli';
|
|
2
|
+
import { RootIndex } from '@travetto/manifest';
|
|
3
|
+
import { ExecUtil, GlobalEnvConfig } from '@travetto/base';
|
|
4
|
+
|
|
5
|
+
type Options = {
|
|
6
|
+
output: OptionConfig<string>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* CLI for outputting the open api spec to a local file
|
|
11
|
+
*/
|
|
12
|
+
export class OpenApiSpecCommand extends CliCommand<Options> {
|
|
13
|
+
name = 'openapi:spec';
|
|
14
|
+
|
|
15
|
+
getOptions(): Options {
|
|
16
|
+
return { output: this.option({ desc: 'Output files', def: './openapi.yml' }) };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
envInit(): GlobalEnvConfig {
|
|
20
|
+
return {
|
|
21
|
+
debug: false,
|
|
22
|
+
set: { API_SPEC_OUTPUT: this.cmd.output }
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async action(): Promise<void> {
|
|
27
|
+
const result = await ExecUtil.worker(
|
|
28
|
+
RootIndex.resolveFileImport('@travetto/cli/support/cli.ts'),
|
|
29
|
+
['main', '@travetto/openapi/support/bin/generate.ts'],
|
|
30
|
+
{ env: { TRV_OPENAPI_OUTPUT: this.cmd.output, TRV_OPENAPI_PERSIST: '1' } }
|
|
31
|
+
).message;
|
|
32
|
+
|
|
33
|
+
if (this.cmd.output === '-' || !this.cmd.output) {
|
|
34
|
+
console.log!(result);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
File without changes
|
package/bin/cli-openapi_spec.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { CliCommand, OptionConfig } from '@travetto/cli/src/command';
|
|
2
|
-
import { ExecUtil } from '@travetto/boot';
|
|
3
|
-
import { EnvInit } from '@travetto/base/bin/init';
|
|
4
|
-
|
|
5
|
-
type Options = {
|
|
6
|
-
output: OptionConfig<string>;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* CLI for outputting the open api spec to a local file
|
|
11
|
-
*/
|
|
12
|
-
export class OpenApiSpecCommand extends CliCommand<Options> {
|
|
13
|
-
name = 'openapi:spec';
|
|
14
|
-
|
|
15
|
-
getOptions(): Options {
|
|
16
|
-
return { output: this.option({ desc: 'Output files', def: './openapi.yml' }) };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
envInit(): void {
|
|
20
|
-
EnvInit.init({
|
|
21
|
-
debug: '0',
|
|
22
|
-
set: { API_SPEC_OUTPUT: this.cmd.output }
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async action(): Promise<void> {
|
|
27
|
-
const result = await ExecUtil.workerMain(require.resolve('./generate')).message;
|
|
28
|
-
|
|
29
|
-
if (this.cmd.output === '-' || !this.cmd.output) {
|
|
30
|
-
console.log!(result);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
package/bin/generate.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ExecUtil } from '@travetto/boot';
|
|
2
|
-
|
|
3
|
-
export async function main(): Promise<void> {
|
|
4
|
-
const { PhaseManager } = await import('@travetto/base');
|
|
5
|
-
await PhaseManager.run('init');
|
|
6
|
-
|
|
7
|
-
const { DependencyRegistry } = await import('@travetto/di');
|
|
8
|
-
const { OpenApiService } = await import('../src/service');
|
|
9
|
-
|
|
10
|
-
const instance = await DependencyRegistry.getInstance(OpenApiService);
|
|
11
|
-
ExecUtil.mainResponse(await instance.spec);
|
|
12
|
-
}
|