@travetto/openapi 3.0.3 → 3.1.0-rc.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/README.md CHANGED
@@ -24,9 +24,9 @@ All of the high level configurations can be found in the following structure:
24
24
 
25
25
  **Code: Config: OpenAPI Configuration**
26
26
  ```typescript
27
- import type { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts/src/model/OpenApi';
27
+ import type { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts';
28
28
 
29
- import { Config, EnvVar } from '@travetto/config';
29
+ import { Config } from '@travetto/config';
30
30
  import { path, RootIndex } from '@travetto/manifest';
31
31
  import { GlobalEnv } from '@travetto/base';
32
32
  import { Required } from '@travetto/schema';
@@ -82,12 +82,10 @@ export class ApiSpecConfig {
82
82
  /**
83
83
  * Where to output file to
84
84
  */
85
- @EnvVar('TRV_OPENAPI_OUTPUT')
86
85
  output: string = 'openapi.yml';
87
86
  /**
88
87
  * Should file be generated at runtime
89
88
  */
90
- @EnvVar('TRV_OPENAPI_PERSIST')
91
89
  persist?: boolean;
92
90
  /**
93
91
  * Skip emitting all routes
@@ -124,10 +122,11 @@ The module provides a command for the [Command Line Interface](https://github.co
124
122
  ```bash
125
123
  $ trv openapi:spec --help
126
124
 
127
- Usage: openapi:spec [options]
125
+ Usage: openapi:spec [options]
128
126
 
129
127
  Options:
130
- -o, --output <output> Output files (default: "./openapi.yml")
128
+ -o, --output <string> Output files
129
+ -m, --module <string> Module to run for
131
130
  -h, --help display help for command
132
131
  ```
133
132
 
@@ -142,19 +141,16 @@ The module provides a command for the [Command Line Interface](https://github.co
142
141
  ```bash
143
142
  $ trv openapi:client --help
144
143
 
145
- Usage: openapi:client [options] <format-or-preset>
144
+ Usage: openapi:client [options] <format:string>
146
145
 
147
146
  Options:
148
- -x, --extended-help Show Extended Help
149
- -a, --additional-properties <additional-properties> Additional Properties (default: [])
150
- -i, --input <input> Input file (default:
151
- "./openapi.yml")
152
- -o, --output <output> Output folder (default:
153
- "./api-client")
154
- -d, --docker-image <docker-image> Docker Image to use (default:
155
- "arcsine/openapi-generator:latest")
156
- -w, --watch Watch for file changes
157
- -h, --help display help for command
147
+ -x, --extended-help Show Extended Help
148
+ -a, --additional-properties <string> Additional Properties (default: [])
149
+ -i, --input <string> Input file (default: "./openapi.yml")
150
+ -o, --output <string> Output folder (default: "./api-client")
151
+ -d, --docker-image <string> Docker Image to user (default: "arcsine/openapi-generator:latest")
152
+ -w, --watch Watch for file changes
153
+ -h, --help display help for command
158
154
 
159
155
  Available Presets
160
156
  ----------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/openapi",
3
- "version": "3.0.3",
3
+ "version": "3.1.0-rc.0",
4
4
  "description": "OpenAPI integration support for the Travetto framework",
5
5
  "keywords": [
6
6
  "rest",
@@ -26,14 +26,14 @@
26
26
  "directory": "module/openapi"
27
27
  },
28
28
  "dependencies": {
29
- "@travetto/config": "^3.0.3",
30
- "@travetto/rest": "^3.0.3",
31
- "@travetto/schema": "^3.0.3",
32
- "@travetto/yaml": "^3.0.3",
29
+ "@travetto/config": "^3.1.0-rc.0",
30
+ "@travetto/rest": "^3.1.0-rc.0",
31
+ "@travetto/schema": "^3.1.0-rc.0",
32
+ "@travetto/yaml": "^3.1.0-rc.0",
33
33
  "openapi3-ts": "^3.1.2"
34
34
  },
35
35
  "peerDependencies": {
36
- "@travetto/cli": "^3.0.3"
36
+ "@travetto/cli": "^3.1.0-rc.0"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@travetto/cli": {
package/src/config.ts CHANGED
@@ -1,11 +1,10 @@
1
- import type { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts/src/model/OpenApi';
1
+ import type { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts';
2
2
 
3
- import { Config, EnvVar } from '@travetto/config';
3
+ import { Config } from '@travetto/config';
4
4
  import { path, RootIndex } from '@travetto/manifest';
5
5
  import { GlobalEnv } from '@travetto/base';
6
6
  import { Required } from '@travetto/schema';
7
7
 
8
-
9
8
  /**
10
9
  * API Information, infers as much as possible from the package.json
11
10
  */
@@ -57,12 +56,10 @@ export class ApiSpecConfig {
57
56
  /**
58
57
  * Where to output file to
59
58
  */
60
- @EnvVar('TRV_OPENAPI_OUTPUT')
61
59
  output: string = 'openapi.yml';
62
60
  /**
63
61
  * Should file be generated at runtime
64
62
  */
65
- @EnvVar('TRV_OPENAPI_PERSIST')
66
63
  persist?: boolean;
67
64
  /**
68
65
  * Skip emitting all routes
package/src/service.ts CHANGED
@@ -65,7 +65,7 @@ export class OpenApiService {
65
65
  ...new SpecGenerator().generate(this.apiSpecConfig)
66
66
  };
67
67
  }
68
- return this._spec;
68
+ return this._spec!;
69
69
  }
70
70
 
71
71
  /**
@@ -2,7 +2,7 @@ import { Readable } from 'stream';
2
2
  import type {
3
3
  SchemaObject, SchemasObject, ParameterObject, OperationObject,
4
4
  RequestBodyObject, TagObject, PathsObject
5
- } from 'openapi3-ts/src/model/OpenApi';
5
+ } from 'openapi3-ts';
6
6
 
7
7
  import { ControllerRegistry, EndpointConfig, ControllerConfig, ParamConfig, EndpointIOType } from '@travetto/rest';
8
8
  import { Class } from '@travetto/base';
@@ -0,0 +1,57 @@
1
+ import fs from 'fs/promises';
2
+
3
+ import { path, RootIndex } from '@travetto/manifest';
4
+ import { cliTpl } from '@travetto/cli';
5
+ import { OpenApiClientPresets } from './presets';
6
+ import { ExecUtil } from '@travetto/base';
7
+
8
+ /**
9
+ * Help utility for openapi client command
10
+ */
11
+ export class OpenApiClientHelp {
12
+
13
+ static async getListOfFormats(dockerImage: string): Promise<string[]> {
14
+ const formatCache = path.resolve(RootIndex.manifest.workspacePath, RootIndex.manifest.toolFolder, 'trv-openapi-formats.json');
15
+ if (!await fs.stat(formatCache).catch(() => false)) {
16
+ const stdout = ExecUtil.spawn('docker', ['run', '--rm', dockerImage, 'list']);
17
+ const res = await stdout.result;
18
+ const lines = res.stdout
19
+ .split('DOCUMENTATION')[0]
20
+ .trim()
21
+ .split(/\n/g)
22
+ .filter(x => /^\s+-/.test(x) && !/\((beta|experimental)\)/.test(x))
23
+ .map(x => x.replace(/^\s+-\s+/, '').trim());
24
+
25
+ await fs.mkdir(path.dirname(formatCache), { recursive: true });
26
+ await fs.writeFile(formatCache, JSON.stringify([...lines.sort(),]));
27
+ }
28
+ const list: string[] = JSON.parse(await fs.readFile(formatCache, 'utf8'));
29
+ return list;
30
+ }
31
+
32
+ static async help(dockerImage: string, extendedHelp: boolean): Promise<string[]> {
33
+ const presets = await OpenApiClientPresets.getPresets();
34
+ const presetLen = Math.max(...Object.keys(presets).map(x => x.length));
35
+ const presetEntries = Object
36
+ .entries(presets)
37
+ .sort(([a], [b]) => a.localeCompare(b))
38
+ .map(([k, [cmd, v]]) => [`@travetto/${k}`.padEnd(presetLen + 5), [cmd, OpenApiClientPresets.presetMap(v)]] as const);
39
+
40
+ const presetText = [
41
+ cliTpl`${{ subtitle: 'Available Presets' }}`,
42
+ '----------------------------------',
43
+ ...presetEntries.map(([k, [cmd, param]]) => cliTpl`* ${{ input: k }} -- ${{ identifier: cmd }} ${{ param }}`),
44
+ ];
45
+
46
+ if (extendedHelp) {
47
+ const formats = await this.getListOfFormats(dockerImage);
48
+ presetText.push(
49
+ '',
50
+ cliTpl`${{ subtitle: 'Available Formats' }}`,
51
+ '----------------------------------',
52
+ ...formats.map(x => cliTpl`* ${{ input: x }}`)
53
+ );
54
+ }
55
+ return presetText;
56
+ }
57
+ }
@@ -0,0 +1,22 @@
1
+ import { FileResourceProvider } from '@travetto/base';
2
+
3
+ /**
4
+ * Presets utility for openapi client command
5
+ */
6
+ export class OpenApiClientPresets {
7
+
8
+ static #presets: Record<string, [string, object] | [string]>;
9
+ static #resources = new FileResourceProvider(['@travetto/openapi#support/resources']);
10
+
11
+ static async getPresets(): Promise<Record<string, [string, object] | [string]>> {
12
+ if (!this.#presets) {
13
+ const text = await this.#resources.read('presets.json');
14
+ this.#presets = JSON.parse(text);
15
+ }
16
+ return this.#presets;
17
+ }
18
+
19
+ static presetMap(prop?: object): string {
20
+ return !prop || Object.keys(prop).length === 0 ? '' : Object.entries(prop).map(([k, v]) => `${k}=${v}`).join(',');
21
+ }
22
+ }
@@ -1,130 +1,68 @@
1
1
  import fs from 'fs/promises';
2
- import { existsSync, readFileSync, writeFileSync } from 'fs';
3
- import cp from 'child_process';
4
2
 
5
- import { path, RootIndex } from '@travetto/manifest';
6
- import { ExecUtil, FileResourceProvider } from '@travetto/base';
7
- import { CliCommand, cliTpl, OptionConfig, ListOptionConfig } from '@travetto/cli';
3
+ import { path } from '@travetto/manifest';
4
+ import { ExecUtil, ShutdownManager } from '@travetto/base';
5
+ import { CliCommandShape, CliCommand, CliFlag } from '@travetto/cli';
6
+
7
+ import { OpenApiClientHelp } from './bin/help';
8
+ import { OpenApiClientPresets } from './bin/presets';
8
9
 
9
- type Options = {
10
- extendedHelp: OptionConfig<boolean>;
11
- props: ListOptionConfig<string>;
12
- input: OptionConfig<string>;
13
- output: OptionConfig<string>;
14
- dockerImage: OptionConfig<string>;
15
- watch: OptionConfig<boolean>;
16
- };
17
10
  /**
18
11
  * CLI for generating the cli client
19
12
  */
20
- export class OpenApiClientCommand extends CliCommand<Options> {
21
- #presets: Record<string, [string, object] | [string]>;
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
- }
32
-
33
- presetMap(prop?: object): string {
34
- return !prop || Object.keys(prop).length === 0 ? '' : Object.entries(prop).map(([k, v]) => `${k}=${v}`).join(',');
35
- }
36
-
37
- getListOfFormats(): string[] {
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();
41
- const lines = stdout
42
- .split('DOCUMENTATION')[0]
43
- .trim()
44
- .split(/\n/g)
45
- .filter(x => /^\s+-/.test(x) && !/\((beta|experimental)\)/.test(x))
46
- .map(x => x.replace(/^\s+-\s+/, '').trim());
47
-
48
- writeFileSync(formatCache, JSON.stringify([...lines.sort(),]));
49
- }
50
- const list: string[] = JSON.parse(readFileSync(formatCache, 'utf8'));
51
- return list;
52
- }
53
-
54
- getOptions(): Options {
55
- return {
56
- extendedHelp: this.boolOption({ name: 'extended-help', short: 'x', desc: 'Show Extended Help' }),
57
- props: this.listOption({ name: 'additional-properties', short: 'a', desc: 'Additional Properties' }),
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 }),
60
- dockerImage: this.option({ desc: 'Docker Image to use', def: 'arcsine/openapi-generator:latest' }),
61
- watch: this.boolOption({ desc: 'Watch for file changes' })
62
- };
63
- }
64
-
65
- async help(): Promise<string> {
66
- const presets = await this.getPresets();
67
- const presetLen = Math.max(...Object.keys(presets).map(x => x.length));
68
- const presetEntries = Object
69
- .entries(presets)
70
- .sort(([a], [b]) => a.localeCompare(b))
71
- .map(([k, [cmd, v]]) => [`@travetto/${k}`.padEnd(presetLen + 5), [cmd, this.presetMap(v)]] as const);
72
-
73
- const presetText = cliTpl`
74
- ${{ subtitle: 'Available Presets' }}
75
- ----------------------------------
76
- ${presetEntries.map(([k, [cmd, param]]) => cliTpl`* ${{ input: k }} -- ${{ identifier: cmd }} ${{ param }}`).join('\n')}`;
77
-
78
- const formatText = cliTpl`
79
- ${{ subtitle: 'Available Formats' }}
80
- ----------------------------------
81
- ${this.getListOfFormats().map(x => cliTpl`* ${{ input: x }}`).join('\n')} `;
82
-
83
- return this.cmd.extendedHelp ? `${presetText}\n${formatText}` : presetText;
84
- }
85
-
86
- getArgs(): string {
87
- return '<format-or-preset>';
13
+ @CliCommand()
14
+ export class OpenApiClientCommand implements CliCommandShape {
15
+ @CliFlag({ desc: 'Show Extended Help', short: '-x' })
16
+ extendedHelp?: boolean;
17
+ @CliFlag({ desc: 'Additional Properties', short: '-a', name: '--additional-properties' })
18
+ props: string[] = [];
19
+ @CliFlag({ desc: 'Input file' })
20
+ input = './openapi.yml';
21
+ @CliFlag({ desc: 'Output folder' })
22
+ output = './api-client';
23
+ @CliFlag({ desc: 'Docker Image to user' })
24
+ dockerImage = 'arcsine/openapi-generator:latest';
25
+ @CliFlag({ desc: 'Watch for file changes' })
26
+ watch?: boolean;
27
+
28
+ async help(): Promise<string[]> {
29
+ return OpenApiClientHelp.help(this.dockerImage, this.extendedHelp ?? false);
88
30
  }
89
31
 
90
- async action(format: string): Promise<void> {
91
- if (!format) {
92
- return this.showHelp('Format is required');
93
- }
94
-
32
+ async main(format: string): Promise<void> {
95
33
  // Ensure its there
96
- await fs.mkdir(this.cmd.output, { recursive: true });
34
+ await fs.mkdir(this.output, { recursive: true });
97
35
 
98
- let propMap = Object.fromEntries(this.cmd.props?.map(p => p.split('=')) ?? []);
36
+ let propMap = Object.fromEntries(this.props?.map(p => p.split('=')) ?? []);
99
37
 
100
38
  if (format.startsWith('@travetto/')) {
101
39
  const key = format.split('@travetto/')[1];
102
- const [fmt, props] = (await this.getPresets())[key];
40
+ const [fmt, props] = (await OpenApiClientPresets.getPresets())[key];
103
41
  format = fmt;
104
42
  propMap = { ...props, ...propMap };
105
43
  }
106
44
 
107
- const propList = this.presetMap(propMap);
45
+ const propList = OpenApiClientPresets.presetMap(propMap);
108
46
 
109
47
  const args = [
110
48
  'run',
111
49
  '--user', `${process.geteuid?.()}:${process.getgid?.()}`,
112
- '-v', `${this.cmd.output}:/workspace`,
113
- '-v', `${path.dirname(this.cmd.input)}:/input`,
50
+ '-v', `${this.output}:/workspace`,
51
+ '-v', `${path.dirname(this.input)}:/input`,
114
52
  '-it',
115
53
  '--rm',
116
- this.cmd.dockerImage,
54
+ this.dockerImage,
117
55
  'generate',
118
56
  '--skip-validate-spec',
119
57
  '--remove-operation-id-prefix',
120
58
  '-g', format,
121
59
  '-o', '/workspace',
122
- '-i', `/input/${path.basename(this.cmd.input)}`,
123
- ...(this.cmd.watch ? ['-w'] : []),
60
+ '-i', `/input/${path.basename(this.input)}`,
61
+ ...(this.watch ? ['-w'] : []),
124
62
  ...(propList ? ['--additional-properties', propList] : [])
125
63
  ];
126
64
 
127
65
  const { result } = ExecUtil.spawn('docker', args, { stdio: [0, 1, 2] });
128
- await result.catch(err => this.exit(1));
66
+ await result.catch(err => ShutdownManager.exit(1));
129
67
  }
130
68
  }
@@ -1,37 +1,37 @@
1
- import { CliCommand, OptionConfig } from '@travetto/cli';
2
- import { RootIndex } from '@travetto/manifest';
3
- import { ExecUtil, GlobalEnvConfig } from '@travetto/base';
1
+ import fs from 'fs/promises';
4
2
 
5
- type Options = {
6
- output: OptionConfig<string>;
7
- };
3
+ import { CliCommandShape, CliCommand } from '@travetto/cli';
4
+ import { GlobalEnvConfig } from '@travetto/base';
5
+ import { RootRegistry } from '@travetto/registry';
6
+ import { DependencyRegistry } from '@travetto/di';
7
+ import { path } from '@travetto/manifest';
8
8
 
9
9
  /**
10
10
  * CLI for outputting the open api spec to a local file
11
11
  */
12
- export class OpenApiSpecCommand extends CliCommand<Options> {
13
- name = 'openapi:spec';
12
+ @CliCommand({ fields: ['module'] })
13
+ export class OpenApiSpecCommand implements CliCommandShape {
14
14
 
15
- getOptions(): Options {
16
- return { output: this.option({ desc: 'Output files', def: './openapi.yml' }) };
17
- }
15
+ /** Output files */
16
+ output?: string;
18
17
 
19
18
  envInit(): GlobalEnvConfig {
20
- return {
21
- debug: false,
22
- set: { API_SPEC_OUTPUT: this.cmd.output }
23
- };
19
+ return { debug: false };
24
20
  }
25
21
 
26
- async action(): Promise<void> {
27
- const result = await ExecUtil.worker(
28
- RootIndex.resolveFileImport('@travetto/cli/support/entry.cli.ts'),
29
- ['main', '@travetto/openapi/support/bin/generate.ts'],
30
- { env: { TRV_OPENAPI_OUTPUT: this.cmd.output, TRV_OPENAPI_PERSIST: '1' } }
31
- ).message;
22
+ async main(): Promise<void> {
23
+ const { OpenApiService } = await import('../src/service.js');
24
+
25
+ await RootRegistry.init();
26
+
27
+ const instance = await DependencyRegistry.getInstance(OpenApiService);
28
+ const result = instance.spec;
32
29
 
33
- if (this.cmd.output === '-' || !this.cmd.output) {
34
- console.log!(result);
30
+ if (this.output === '-' || !this.output) {
31
+ console.log!(JSON.stringify(result, null, 2));
32
+ } else {
33
+ await fs.mkdir(path.dirname(this.output), { recursive: true });
34
+ await fs.writeFile(this.output, JSON.stringify(result, null, 2), 'utf8');
35
35
  }
36
36
  }
37
37
  }
@@ -1,11 +0,0 @@
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
- }