@travetto/openapi 6.0.0-rc.0 → 6.0.0-rc.2

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
@@ -13,9 +13,9 @@ npm install @travetto/openapi
13
13
  yarn add @travetto/openapi
14
14
  ```
15
15
 
16
- 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.
16
+ In the [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.") 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.
17
17
 
18
- 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.
18
+ The module is provides an [OpenAPI](https://github.com/OAI/OpenAPI-Specification) v3.x representation of the API metadata provided via the [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.") and [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") modules.
19
19
 
20
20
  ## Configuration
21
21
  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).
@@ -58,7 +58,7 @@ export class ApiInfoConfig {
58
58
  }
59
59
 
60
60
  /**
61
- * The API host, infers from rest host configuration
61
+ * The API host, infers from web host configuration
62
62
  */
63
63
  @Config('api.host')
64
64
  export class ApiHostConfig {
@@ -86,9 +86,9 @@ export class ApiSpecConfig {
86
86
  */
87
87
  persist?: boolean;
88
88
  /**
89
- * Skip emitting all routes
89
+ * Skip emitting all endpoints
90
90
  */
91
- skipRoutes: boolean = false;
91
+ skipEndpoints: boolean = false;
92
92
  /**
93
93
  * Expose all schemas, even if not referenced
94
94
  */
@@ -128,9 +128,9 @@ Options:
128
128
  -h, --help display help for command
129
129
  ```
130
130
 
131
- The command will run your application, in non-server mode, to collect all the routes and model information, to produce the `openapi.yml`. Once produced, the code will store the output in the specified location.
131
+ The command will run your application, in non-server mode, to collect all the endpoints and model information, to produce the `openapi.yml`. Once produced, the code will store the output in the specified location.
132
132
 
133
- **Note**: The module supports generating the OpenAPI spec in real-time while listening for changes to routes and models.
133
+ **Note**: The module supports generating the OpenAPI spec in real-time while listening for changes to endpoints and models.
134
134
 
135
135
  ## CLI - openapi:client
136
136
  The module provides a command for the [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework") to allow client generation from the API structure.
package/__index__.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from './src/spec-generate';
2
- export * from './src/service';
3
- export * from './src/config';
1
+ export * from './src/spec-generate.ts';
2
+ export * from './src/service.ts';
3
+ export * from './src/config.ts';
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@travetto/openapi",
3
- "version": "6.0.0-rc.0",
3
+ "version": "6.0.0-rc.2",
4
4
  "description": "OpenAPI integration support for the Travetto framework",
5
5
  "keywords": [
6
- "rest",
6
+ "web",
7
7
  "travetto",
8
8
  "decorators",
9
9
  "schema",
@@ -26,14 +26,14 @@
26
26
  "directory": "module/openapi"
27
27
  },
28
28
  "dependencies": {
29
- "@travetto/config": "^6.0.0-rc.0",
30
- "@travetto/rest": "^6.0.0-rc.0",
31
- "@travetto/schema": "^6.0.0-rc.0",
29
+ "@travetto/config": "^6.0.0-rc.2",
30
+ "@travetto/schema": "^6.0.0-rc.2",
31
+ "@travetto/web": "^6.0.0-rc.2",
32
32
  "openapi3-ts": "^4.4.0",
33
- "yaml": "^2.7.0"
33
+ "yaml": "^2.7.1"
34
34
  },
35
35
  "peerDependencies": {
36
- "@travetto/cli": "^6.0.0-rc.0"
36
+ "@travetto/cli": "^6.0.0-rc.2"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@travetto/cli": {
package/src/config.ts CHANGED
@@ -32,7 +32,7 @@ export class ApiInfoConfig {
32
32
  }
33
33
 
34
34
  /**
35
- * The API host, infers from rest host configuration
35
+ * The API host, infers from web host configuration
36
36
  */
37
37
  @Config('api.host')
38
38
  export class ApiHostConfig {
@@ -60,9 +60,9 @@ export class ApiSpecConfig {
60
60
  */
61
61
  persist?: boolean;
62
62
  /**
63
- * Skip emitting all routes
63
+ * Skip emitting all endpoints
64
64
  */
65
- skipRoutes: boolean = false;
65
+ skipEndpoints: boolean = false;
66
66
  /**
67
67
  * Expose all schemas, even if not referenced
68
68
  */
package/src/controller.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { stringify } from 'yaml';
2
2
 
3
- import { ConfigureInterceptor, Controller, CorsInterceptor, Get, SetHeaders, Undocumented } from '@travetto/rest';
3
+ import { ConfigureInterceptor, Controller, CorsInterceptor, Get, SetHeaders, Undocumented } from '@travetto/web';
4
4
  import { Inject } from '@travetto/di';
5
5
 
6
- import { OpenApiService } from './service';
6
+ import { OpenApiService } from './service.ts';
7
7
 
8
8
  /**
9
9
  * Basic controller for surfacing the api spec
package/src/service.ts CHANGED
@@ -3,11 +3,11 @@ import { stringify } from 'yaml';
3
3
 
4
4
  import { BinaryUtil } from '@travetto/runtime';
5
5
  import { Injectable, Inject } from '@travetto/di';
6
- import { ControllerRegistry, ControllerVisitUtil, RestConfig } from '@travetto/rest';
6
+ import { ControllerRegistry, ControllerVisitUtil, WebConfig } from '@travetto/web';
7
7
  import { SchemaRegistry } from '@travetto/schema';
8
8
 
9
- import { ApiHostConfig, ApiInfoConfig, ApiSpecConfig } from './config';
10
- import { OpenapiVisitor } from './spec-generate';
9
+ import { ApiHostConfig, ApiInfoConfig, ApiSpecConfig } from './config.ts';
10
+ import { OpenapiVisitor } from './spec-generate.ts';
11
11
 
12
12
  /**
13
13
  * Open API generation service
@@ -25,7 +25,7 @@ export class OpenApiService {
25
25
  apiSpecConfig: ApiSpecConfig;
26
26
 
27
27
  @Inject()
28
- restConfig: RestConfig;
28
+ webConfig: WebConfig;
29
29
 
30
30
  #spec: OpenAPIObject | undefined;
31
31
 
@@ -46,8 +46,8 @@ export class OpenApiService {
46
46
  ControllerRegistry.on(() => this.resetSpec());
47
47
  SchemaRegistry.on(() => this.resetSpec());
48
48
 
49
- if (!this.apiHostConfig.servers) {
50
- this.apiHostConfig.servers = [{ url: this.restConfig.baseUrl }];
49
+ if (!this.apiHostConfig.servers && this.webConfig.baseUrl) {
50
+ this.apiHostConfig.servers = [{ url: this.webConfig.baseUrl }];
51
51
  }
52
52
 
53
53
  await this.resetSpec();
@@ -4,12 +4,11 @@ import type {
4
4
  RequestBodyObject, TagObject, PathsObject, PathItemObject
5
5
  } from 'openapi3-ts/oas31';
6
6
 
7
- import { EndpointConfig, ControllerConfig, ParamConfig, EndpointIOType, ControllerVisitor } from '@travetto/rest';
7
+ import { EndpointConfig, ControllerConfig, EndpointParamConfig, EndpointIOType, ControllerVisitor, HTTP_METHODS } from '@travetto/web';
8
8
  import { Class, describeFunction } from '@travetto/runtime';
9
9
  import { SchemaRegistry, FieldConfig, ClassConfig, SchemaNameResolver } from '@travetto/schema';
10
- import { AllViewSymbol } from '@travetto/schema/src/internal/types';
11
10
 
12
- import { ApiSpecConfig } from './config';
11
+ import { ApiSpecConfig } from './config.ts';
13
12
 
14
13
  const DEFINITION = '#/components/schemas';
15
14
 
@@ -42,17 +41,6 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
42
41
  this.#config = config;
43
42
  }
44
43
 
45
- /**
46
- * Build response object
47
- */
48
- #getHeaderValue(ep: EndpointConfig, header: string): string | undefined {
49
- let cType = ep.headers?.[header];
50
- if (cType && typeof cType !== 'string') {
51
- cType = cType();
52
- }
53
- return cType;
54
- }
55
-
56
44
  /**
57
45
  * Convert schema to a set of dotted parameters
58
46
  */
@@ -206,7 +194,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
206
194
  };
207
195
 
208
196
  const properties: Record<string, SchemaObject> = {};
209
- const def = config.views[AllViewSymbol];
197
+ const def = config.totalView;
210
198
  const required: string[] = [];
211
199
 
212
200
  for (const fieldName of def.fields) {
@@ -243,7 +231,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
243
231
  /**
244
232
  * Standard payload structure
245
233
  */
246
- #getEndpointBody(body?: EndpointIOType, mime?: string): RequestBodyObject {
234
+ #getEndpointBody(body?: EndpointIOType, mime?: string | null): RequestBodyObject {
247
235
  if (!body) {
248
236
  return { content: {}, description: '' };
249
237
  } else if (body.type === Readable || body.type === Buffer) {
@@ -271,7 +259,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
271
259
  /**
272
260
  * Process endpoint parameter
273
261
  */
274
- #processEndpointParam(ep: EndpointConfig, param: ParamConfig, field: FieldConfig): (
262
+ #processEndpointParam(ep: EndpointConfig, param: EndpointParamConfig, field: FieldConfig): (
275
263
  { requestBody: RequestBodyObject } |
276
264
  { parameters: ParameterObject[] } |
277
265
  undefined
@@ -279,12 +267,13 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
279
267
  const complex = field.type && SchemaRegistry.has(field.type);
280
268
  if (param.location) {
281
269
  if (param.location === 'body') {
270
+ const acceptsMime = ep.responseHeaderMap.get('accepts');
282
271
  return {
283
- requestBody: field.specifiers?.includes('file') ? this.#buildUploadBody() : this.#getEndpointBody(field, this.#getHeaderValue(ep, 'accepts'))
272
+ requestBody: field.specifiers?.includes('file') ? this.#buildUploadBody() : this.#getEndpointBody(field, acceptsMime)
284
273
  };
285
274
  } else if (complex && (param.location === 'query' || param.location === 'header')) {
286
275
  return { parameters: this.#schemaToDotParams(param.location, field, param.prefix ? `${param.prefix}.` : '') };
287
- } else if (param.location !== 'context') {
276
+ } else {
288
277
  const epParam: ParameterObject = {
289
278
  in: param.location,
290
279
  name: param.name || param.location,
@@ -293,8 +282,6 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
293
282
  schema: field.array ? { type: 'array', items: this.#getType(field) } : this.#getType(field)
294
283
  };
295
284
  return { parameters: [epParam] };
296
- } else if (field.specifiers?.includes('file')) {
297
- return { requestBody: this.#buildUploadBody() };
298
285
  }
299
286
  }
300
287
  }
@@ -303,7 +290,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
303
290
  * Process controller endpoint
304
291
  */
305
292
  onEndpointEnd(ep: EndpointConfig, ctrl: ControllerConfig): void {
306
- if (this.#config.skipRoutes) {
293
+ if (this.#config.skipEndpoints || !ep.httpMethod) {
307
294
  return;
308
295
  }
309
296
 
@@ -314,43 +301,37 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
314
301
  responses: {},
315
302
  summary: ep.title,
316
303
  description: ep.description || ep.title,
317
- operationId: `${ep.class.name}_${ep.handlerName}`,
304
+ operationId: `${ep.class.name}_${ep.name}`,
318
305
  parameters: []
319
306
  };
320
307
 
321
- const pConf = this.#getEndpointBody(ep.responseType, this.#getHeaderValue(ep, 'content-type'));
308
+ const contentTypeMime = ep.responseHeaderMap.get('content-type');
309
+ const pConf = this.#getEndpointBody(ep.responseType, contentTypeMime);
322
310
  const code = Object.keys(pConf.content).length ? 200 : 201;
323
311
  op.responses![code] = pConf;
324
312
 
325
- const schema = SchemaRegistry.getMethodSchema(ep.class, ep.handlerName);
313
+ const schema = SchemaRegistry.getMethodSchema(ep.class, ep.name);
326
314
  for (const field of schema) {
327
- const res = this.#processEndpointParam(ep, ep.params[field.index!], field);
328
- if (res) {
329
- if ('parameters' in res) {
330
- (op.parameters ??= []).push(...res.parameters);
315
+ const result = this.#processEndpointParam(ep, ep.params[field.index!], field);
316
+ if (result) {
317
+ if ('parameters' in result) {
318
+ (op.parameters ??= []).push(...result.parameters);
331
319
  } else {
332
- op.requestBody ??= res.requestBody;
320
+ op.requestBody ??= result.requestBody;
333
321
  }
334
322
  }
335
323
  }
336
324
 
337
- const epPath = (!ep.path ? '/' : ep.path).replace(/:([A-Za-z0-9_]+)\b/g, (__, param) => `{${param}}`);
338
-
339
- const key = `${ctrl.basePath}${epPath}`.replace(/[\/]+/g, '/');
340
-
341
- const toAdd = ep.method === 'all' ?
342
- ['get', 'post', 'put', 'delete', 'patch'].reduce((acc, v) =>
343
- ({ ...acc, [v]: { ...op, operationId: `${op.operationId}_${v}` } }), {}) :
344
- { [ep.method]: op };
325
+ const key = ep.fullPath.replace(/:([A-Za-z0-9_]+)\b/g, (__, param) => `{${param}}`);
345
326
 
346
327
  this.#paths[key] = {
347
328
  ...(this.#paths[key] ?? {}),
348
- ...toAdd
329
+ [HTTP_METHODS[ep.httpMethod].lower]: op
349
330
  };
350
331
  }
351
332
 
352
333
  onControllerEnd(controller: ControllerConfig): void {
353
- if (this.#config.skipRoutes) {
334
+ if (this.#config.skipEndpoints) {
354
335
  return;
355
336
  }
356
337
  this.#tags.push({
@@ -365,19 +346,19 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
365
346
  }
366
347
 
367
348
  return {
368
- tags: this.#tags.sort((a, b) => a.name.localeCompare(b.name)),
349
+ tags: this.#tags.toSorted((a, b) => a.name.localeCompare(b.name)),
369
350
  paths: Object.fromEntries(
370
351
  Object.entries(this.#paths)
371
- .sort(([a], [b]) => a.localeCompare(b))
352
+ .toSorted(([a], [b]) => a.localeCompare(b))
372
353
  .map(([k, v]) => [k, Object.fromEntries(
373
354
  Object.entries(v)
374
- .sort(([a], [b]) => a.localeCompare(b))
355
+ .toSorted(([a], [b]) => a.localeCompare(b))
375
356
  )])
376
357
  ),
377
358
  components: {
378
359
  schemas: Object.fromEntries(
379
360
  Object.entries(this.#schemas)
380
- .sort(([a], [b]) => a.localeCompare(b))
361
+ .toSorted(([a], [b]) => a.localeCompare(b))
381
362
  )
382
363
  }
383
364
  };
@@ -22,7 +22,7 @@ export class OpenApiClientHelp {
22
22
  .map(x => x.replace(/^\s+-\s+/, '').trim());
23
23
 
24
24
  await fs.mkdir(path.dirname(formatCache), { recursive: true });
25
- await fs.writeFile(formatCache, JSON.stringify([...lines.sort(),]));
25
+ await fs.writeFile(formatCache, JSON.stringify([...lines.toSorted(),]));
26
26
  }
27
27
  const list: string[] = JSON.parse(await fs.readFile(formatCache, 'utf8'));
28
28
  return list;
@@ -4,7 +4,7 @@ import cp from 'node:child_process';
4
4
  import { CliCommandShape, CliCommand, CliFlag } from '@travetto/cli';
5
5
  import { ExecUtil } from '@travetto/runtime';
6
6
 
7
- import { OpenApiClientHelp } from './bin/help';
7
+ import { OpenApiClientHelp } from './bin/help.ts';
8
8
 
9
9
  /**
10
10
  * CLI for generating the cli client
@@ -20,7 +20,7 @@ export class OpenApiSpecCommand implements CliCommandShape {
20
20
  }
21
21
 
22
22
  async main(): Promise<void> {
23
- const { OpenApiService } = await import('../src/service');
23
+ const { OpenApiService } = await import('../src/service.ts');
24
24
 
25
25
  await RootRegistry.init();
26
26