@travetto/openapi 7.0.0-rc.2 → 7.0.0-rc.4

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
@@ -99,7 +99,7 @@ export class ApiSpecConfig {
99
99
  this.persist = false;
100
100
  } else {
101
101
  this.output = path.resolve(Runtime.mainSourcePath, this.output);
102
- this.persist ??= Runtime.dynamic;
102
+ this.persist ??= !Runtime.production;
103
103
  }
104
104
  if (this.persist) {
105
105
  if (!/[.](json|ya?ml) $/.test(this.output)) { // Assume a folder
package/__index__.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from './src/spec-generate.ts';
1
+ export * from './src/generate.ts';
2
2
  export * from './src/service.ts';
3
3
  export * from './src/config.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/openapi",
3
- "version": "7.0.0-rc.2",
3
+ "version": "7.0.0-rc.4",
4
4
  "description": "OpenAPI integration support for the Travetto framework",
5
5
  "keywords": [
6
6
  "web",
@@ -26,14 +26,14 @@
26
26
  "directory": "module/openapi"
27
27
  },
28
28
  "dependencies": {
29
- "@travetto/config": "^7.0.0-rc.1",
30
- "@travetto/schema": "^7.0.0-rc.1",
31
- "@travetto/web": "^7.0.0-rc.2",
29
+ "@travetto/config": "^7.0.0-rc.3",
30
+ "@travetto/schema": "^7.0.0-rc.3",
31
+ "@travetto/web": "^7.0.0-rc.4",
32
32
  "openapi3-ts": "^4.5.0",
33
33
  "yaml": "^2.8.1"
34
34
  },
35
35
  "peerDependencies": {
36
- "@travetto/cli": "^7.0.0-rc.1"
36
+ "@travetto/cli": "^7.0.0-rc.3"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@travetto/cli": {
package/src/config.ts CHANGED
@@ -73,7 +73,7 @@ export class ApiSpecConfig {
73
73
  this.persist = false;
74
74
  } else {
75
75
  this.output = path.resolve(Runtime.mainSourcePath, this.output);
76
- this.persist ??= Runtime.dynamic;
76
+ this.persist ??= !Runtime.production;
77
77
  }
78
78
  if (this.persist) {
79
79
  if (!/[.](json|ya?ml)$/.test(this.output)) { // Assume a folder
@@ -12,8 +12,8 @@ import { ApiSpecConfig } from './config.ts';
12
12
 
13
13
  const DEFINITION = '#/components/schemas';
14
14
 
15
- const isInputConfig = (val: object): val is SchemaInputConfig => !!val && 'owner' in val && 'type' in val;
16
- const isFieldConfig = (val: object): val is SchemaFieldConfig => isInputConfig(val) && 'name' in val;
15
+ const isInputConfig = (value: object): value is SchemaInputConfig => !!value && 'class' in value && 'type' in value;
16
+ const isFieldConfig = (value: object): value is SchemaFieldConfig => isInputConfig(value) && 'name' in value;
17
17
 
18
18
  type GeneratedSpec = {
19
19
  tags: TagObject[];
@@ -51,7 +51,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
51
51
  const fields = SchemaRegistryIndex.get(input.type).getFields(input.view);
52
52
  const params: ParameterObject[] = [];
53
53
  for (const sub of Object.values(fields)) {
54
- const name = sub.name.toString();
54
+ const name = sub.name;
55
55
  if (SchemaRegistryIndex.has(sub.type)) {
56
56
  const suffix = (sub.array) ? '[]' : '';
57
57
  params.push(...this.#schemaToDotParams(location, sub, prefix ? `${prefix}.${name}${suffix}` : `${name}${suffix}.`, rootField));
@@ -131,48 +131,48 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
131
131
  * Process schema field
132
132
  */
133
133
  #processSchemaField(input: SchemaInputConfig, required: string[]): SchemaObject {
134
- let prop: SchemaObject = this.#getType(input);
134
+ let config: SchemaObject = this.#getType(input);
135
135
 
136
136
  if (input.examples) {
137
- prop.example = input.examples;
137
+ config.example = input.examples;
138
138
  }
139
- prop.description = input.description;
139
+ config.description = input.description;
140
140
  if (input.match) {
141
- prop.pattern = input.match.re!.source;
141
+ config.pattern = input.match.regex!.source;
142
142
  }
143
143
  if (input.maxlength) {
144
- prop.maxLength = input.maxlength.n;
144
+ config.maxLength = input.maxlength.limit;
145
145
  }
146
146
  if (input.minlength) {
147
- prop.minLength = input.minlength.n;
147
+ config.minLength = input.minlength.limit;
148
148
  }
149
149
  if (input.min) {
150
- prop.minimum = typeof input.min.n === 'number' ? input.min.n : input.min.n.getTime();
150
+ config.minimum = typeof input.min.limit === 'number' ? input.min.limit : input.min.limit.getTime();
151
151
  }
152
152
  if (input.max) {
153
- prop.maximum = typeof input.max.n === 'number' ? input.max.n : input.max.n.getTime();
153
+ config.maximum = typeof input.max.limit === 'number' ? input.max.limit : input.max.limit.getTime();
154
154
  }
155
155
  if (input.enum) {
156
- prop.enum = input.enum.values;
156
+ config.enum = input.enum.values;
157
157
  }
158
158
  if (isFieldConfig(input)) {
159
159
  if (input.required?.active !== false) {
160
- required.push(input.name.toString());
160
+ required.push(input.name);
161
161
  }
162
162
  if (input.access === 'readonly') {
163
- prop.readOnly = true;
163
+ config.readOnly = true;
164
164
  } else if (input.access === 'writeonly') {
165
- prop.writeOnly = true;
165
+ config.writeOnly = true;
166
166
  }
167
167
  }
168
168
  if (input.array) {
169
- prop = {
169
+ config = {
170
170
  type: 'array',
171
- items: prop
171
+ items: config
172
172
  };
173
173
  }
174
174
 
175
- return prop;
175
+ return config;
176
176
  }
177
177
 
178
178
  /**
@@ -195,25 +195,25 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
195
195
  };
196
196
 
197
197
  const properties: Record<string, SchemaObject> = {};
198
- const def = config;
198
+ const base = config;
199
199
  const required: string[] = [];
200
200
 
201
- for (const fieldName of Object.keys(def.fields)) {
202
- if (SchemaRegistryIndex.has(def.fields[fieldName].type)) {
203
- this.onSchema(SchemaRegistryIndex.getConfig(def.fields[fieldName].type));
201
+ for (const fieldName of Object.keys(base.fields)) {
202
+ if (SchemaRegistryIndex.has(base.fields[fieldName].type)) {
203
+ this.onSchema(SchemaRegistryIndex.getConfig(base.fields[fieldName].type));
204
204
  }
205
- properties[fieldName] = this.#processSchemaField(def.fields[fieldName], required);
205
+ properties[fieldName] = this.#processSchemaField(base.fields[fieldName], required);
206
206
  }
207
207
 
208
208
  const extra: Record<string, unknown> = {};
209
209
  if (config.discriminatedBase) {
210
- const map = SchemaRegistryIndex.getDiscriminatedClasses(cls);
211
- if (map) {
212
- extra.oneOf = map
213
- .filter(x => !describeFunction(x)?.abstract)
214
- .map(c => {
215
- this.onSchema(SchemaRegistryIndex.getConfig(c));
216
- return this.#getType(c);
210
+ const subClasses = SchemaRegistryIndex.getDiscriminatedClasses(cls);
211
+ if (subClasses) {
212
+ extra.oneOf = subClasses
213
+ .filter(subCls => !describeFunction(subCls)?.abstract)
214
+ .map(subCls => {
215
+ this.onSchema(SchemaRegistryIndex.getConfig(subCls));
216
+ return this.#getType(subCls);
217
217
  });
218
218
  }
219
219
  }
@@ -260,7 +260,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
260
260
  /**
261
261
  * Process endpoint parameter
262
262
  */
263
- #processEndpointParam(ep: EndpointConfig, param: EndpointParameterConfig, input: SchemaParameterConfig): (
263
+ #processEndpointParam(endpoint: EndpointConfig, param: EndpointParameterConfig, input: SchemaParameterConfig): (
264
264
  { requestBody: RequestBodyObject } |
265
265
  { parameters: ParameterObject[] } |
266
266
  undefined
@@ -269,7 +269,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
269
269
 
270
270
  if (param.location) {
271
271
  if (param.location === 'body') {
272
- const acceptsMime = ep.finalizedResponseHeaders.get('accepts');
272
+ const acceptsMime = endpoint.finalizedResponseHeaders.get('accepts');
273
273
  return {
274
274
  requestBody: input.specifiers?.includes('file') ? this.#buildUploadBody() : this.#getEndpointBody(input, acceptsMime)
275
275
  };
@@ -291,47 +291,45 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
291
291
  /**
292
292
  * Process controller endpoint
293
293
  */
294
- onEndpointEnd(ep: EndpointConfig, ctrl: ControllerConfig): void {
295
- if (this.#config.skipEndpoints || !ep.httpMethod) {
294
+ onEndpointEnd(endpoint: EndpointConfig, controller: ControllerConfig): void {
295
+ if (this.#config.skipEndpoints || !endpoint.httpMethod) {
296
296
  return;
297
297
  }
298
298
 
299
- const tagName = ctrl.externalName;
299
+ const tagName = controller.externalName;
300
300
 
301
- const schema = SchemaRegistryIndex.get(ep.class).getMethod(ep.methodName);
301
+ const schema = SchemaRegistryIndex.get(endpoint.class).getMethod(endpoint.methodName);
302
302
 
303
- const op: OperationObject = {
303
+ const apiConfig: OperationObject = {
304
304
  tags: [tagName],
305
305
  responses: {},
306
306
  summary: schema.description,
307
307
  description: schema.description,
308
- operationId: `${ep.class.name}_${ep.methodName.toString()}`,
308
+ operationId: `${endpoint.class.name}_${endpoint.methodName}`,
309
309
  parameters: []
310
310
  };
311
311
 
312
- const contentTypeMime = ep.finalizedResponseHeaders.get('content-type');
313
- const pConf = this.#getEndpointBody(schema.returnType, contentTypeMime);
314
- const code = Object.keys(pConf.content).length ? 200 : 201;
315
- op.responses![code] = pConf;
312
+ const contentTypeMime = endpoint.finalizedResponseHeaders.get('content-type');
313
+ const bodyConfig = this.#getEndpointBody(schema.returnType, contentTypeMime);
314
+ const code = Object.keys(bodyConfig.content).length ? 200 : 201;
315
+ apiConfig.responses![code] = bodyConfig;
316
316
 
317
- const methodSchema = SchemaRegistryIndex.get(ep.class).getMethod(ep.methodName);
318
-
319
- for (const param of methodSchema.parameters) {
320
- const result = this.#processEndpointParam(ep, ep.parameters[param.index] ?? {}, param);
317
+ for (const param of schema.parameters) {
318
+ const result = this.#processEndpointParam(endpoint, endpoint.parameters[param.index] ?? {}, param);
321
319
  if (result) {
322
320
  if ('parameters' in result) {
323
- (op.parameters ??= []).push(...result.parameters);
321
+ (apiConfig.parameters ??= []).push(...result.parameters);
324
322
  } else {
325
- op.requestBody ??= result.requestBody;
323
+ apiConfig.requestBody ??= result.requestBody;
326
324
  }
327
325
  }
328
326
  }
329
327
 
330
- const key = ep.fullPath.replace(/:([A-Za-z0-9_]+)\b/g, (__, param) => `{${param}}`);
328
+ const key = endpoint.fullPath.replace(/:([A-Za-z0-9_]+)\b/g, (__, param) => `{${param}}`);
331
329
 
332
330
  this.#paths[key] = {
333
331
  ...(this.#paths[key] ?? {}),
334
- [HTTP_METHODS[ep.httpMethod].lower]: op
332
+ [HTTP_METHODS[endpoint.httpMethod].lower]: apiConfig
335
333
  };
336
334
  }
337
335
 
@@ -356,8 +354,8 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
356
354
  paths: Object.fromEntries(
357
355
  Object.entries(this.#paths)
358
356
  .toSorted(([a], [b]) => a.localeCompare(b))
359
- .map(([k, v]) => [k, Object.fromEntries(
360
- Object.entries(v)
357
+ .map(([key, value]) => [key, Object.fromEntries(
358
+ Object.entries(value)
361
359
  .toSorted(([a], [b]) => a.localeCompare(b))
362
360
  )])
363
361
  ),
package/src/service.ts CHANGED
@@ -3,12 +3,10 @@ import { stringify } from 'yaml';
3
3
 
4
4
  import { BinaryUtil } from '@travetto/runtime';
5
5
  import { Injectable, Inject } from '@travetto/di';
6
- import { ControllerRegistryIndex, ControllerVisitUtil, WebConfig } from '@travetto/web';
7
- import { SchemaRegistryIndex } from '@travetto/schema';
8
- import { Registry } from '@travetto/registry';
6
+ import { ControllerVisitUtil, WebConfig } from '@travetto/web';
9
7
 
10
8
  import { ApiHostConfig, ApiInfoConfig, ApiSpecConfig } from './config.ts';
11
- import { OpenapiVisitor } from './spec-generate.ts';
9
+ import { OpenapiVisitor } from './generate.ts';
12
10
 
13
11
  /**
14
12
  * Open API generation service
@@ -44,9 +42,6 @@ export class OpenApiService {
44
42
  * Initialize after schemas are readied
45
43
  */
46
44
  async postConstruct(): Promise<void> {
47
- Registry.onClassChange(() => this.resetSpec(), ControllerRegistryIndex);
48
- Registry.onClassChange(() => this.resetSpec(), SchemaRegistryIndex);
49
-
50
45
  if (!this.apiHostConfig.servers && this.webConfig.baseUrl) {
51
46
  this.apiHostConfig.servers = [{ url: this.webConfig.baseUrl }];
52
47
  }
@@ -82,8 +77,8 @@ export class OpenApiService {
82
77
  stringify(spec);
83
78
 
84
79
  await BinaryUtil.bufferedFileWrite(this.apiSpecConfig.output, output, true);
85
- } catch (err) {
86
- console.error('Unable to persist openapi spec', err);
80
+ } catch (error) {
81
+ console.error('Unable to persist openapi spec', error);
87
82
  }
88
83
  }
89
84
  }
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import { spawn } from 'node:child_process';
3
3
  import path from 'node:path';
4
4
 
5
- import { ExecUtil, Runtime } from '@travetto/runtime';
5
+ import { ExecUtil, JSONUtil, Runtime } from '@travetto/runtime';
6
6
  import { cliTpl } from '@travetto/cli';
7
7
 
8
8
  /**
@@ -18,13 +18,13 @@ export class OpenApiClientHelp {
18
18
  .split('DOCUMENTATION')[0]
19
19
  .trim()
20
20
  .split(/\n/g)
21
- .filter(x => /^\s+-/.test(x) && !/\((beta|experimental)\)/.test(x))
22
- .map(x => x.replace(/^\s+-\s+/, '').trim());
21
+ .filter(line => /^\s+-/.test(line) && !/\((beta|experimental)\)/.test(line))
22
+ .map(line => line.replace(/^\s+-\s+/, '').trim());
23
23
 
24
24
  await fs.mkdir(path.dirname(formatCache), { recursive: true });
25
25
  await fs.writeFile(formatCache, JSON.stringify([...lines.toSorted(),]));
26
26
  }
27
- const list: string[] = JSON.parse(await fs.readFile(formatCache, 'utf8'));
27
+ const list: string[] = await JSONUtil.readFile(formatCache);
28
28
  return list;
29
29
  }
30
30
 
@@ -36,7 +36,7 @@ export class OpenApiClientHelp {
36
36
  '',
37
37
  cliTpl`${{ subtitle: 'Available Formats' }}`,
38
38
  '----------------------------------',
39
- ...formats.map(x => cliTpl`* ${{ input: x }}`)
39
+ ...formats.map(format => cliTpl`* ${{ input: format }}`)
40
40
  );
41
41
  }
42
42
  return help;
@@ -16,7 +16,7 @@ export class OpenApiClientCommand implements CliCommandShape {
16
16
  extendedHelp: boolean = false;
17
17
  /** Additional Properties */
18
18
  @CliFlag({ short: '-a', full: '--additional-properties' })
19
- props: string[] = [];
19
+ properties: string[] = [];
20
20
  /** Input file */
21
21
  input = './openapi.yml';
22
22
  /** Output folder */
@@ -32,7 +32,7 @@ export class OpenApiClientCommand implements CliCommandShape {
32
32
  this.output = path.resolve(this.output);
33
33
  this.input = path.resolve(this.input);
34
34
 
35
- const proc = cp.spawn('docker', [
35
+ const subProcess = cp.spawn('docker', [
36
36
  'run',
37
37
  '--rm',
38
38
  '-i',
@@ -47,12 +47,12 @@ export class OpenApiClientCommand implements CliCommandShape {
47
47
  '-g', format,
48
48
  '-o', '/workspace',
49
49
  '-i', `/input/${path.basename(this.input)}`,
50
- ...(this.props.length ? ['--additional-properties', this.props.join(',')] : [])
50
+ ...(this.properties.length ? ['--additional-properties', this.properties.join(',')] : [])
51
51
  ], {
52
52
  stdio: 'inherit'
53
53
  });
54
54
 
55
- const result = await ExecUtil.getResult(proc);
55
+ const result = await ExecUtil.getResult(subProcess);
56
56
 
57
57
  if (!result.valid) {
58
58
  process.exitCode = 1;