@rsdk/http.server 6.0.0-next.1 → 6.0.0-next.10

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.
@@ -20,14 +20,14 @@ let HttpConfig = class HttpConfig extends core_1.Config {
20
20
  };
21
21
  exports.HttpConfig = HttpConfig;
22
22
  __decorate([
23
- (0, core_1.Property)('HTTP_HOST', new core_1.StringParser(), {
23
+ (0, core_1.Property)('HTTP_HOST', new common_1.StringParser(), {
24
24
  defaultValue: '0.0.0.0',
25
25
  description: 'HTTP host',
26
26
  }),
27
27
  __metadata("design:type", String)
28
28
  ], HttpConfig.prototype, "host", void 0);
29
29
  __decorate([
30
- (0, core_1.Property)('HTTP_PORT', new core_1.IntParser(), {
30
+ (0, core_1.Property)('HTTP_PORT', new common_1.IntParser(), {
31
31
  // eslint-disable-next-line unicorn/numeric-separators-style
32
32
  defaultValue: 50050,
33
33
  description: 'HTTP port',
@@ -35,16 +35,16 @@ __decorate([
35
35
  __metadata("design:type", Number)
36
36
  ], HttpConfig.prototype, "port", void 0);
37
37
  __decorate([
38
- (0, core_1.Property)('HTTP_BODY_LIMIT', new core_1.SizeParser(), {
38
+ (0, core_1.Property)('HTTP_BODY_LIMIT', new common_1.SizeParser(), {
39
39
  defaultValue: new common_1.Size(100, 'kb'),
40
40
  description: 'Max HTTP body size',
41
41
  }),
42
42
  __metadata("design:type", common_1.Size)
43
43
  ], HttpConfig.prototype, "bodyLimit", void 0);
44
44
  __decorate([
45
- (0, core_1.Property)('HTTP_SWAGGER_UI_ENABLED', new core_1.BoolParser(), {
45
+ (0, core_1.Property)('HTTP_SWAGGER_UI_ENABLED', new common_1.BoolParser(), {
46
46
  defaultValue: true,
47
- description: 'Enable swagger UI on /swagger endpoint',
47
+ description: 'Enable swagger UI on /swagger (can be configured) endpoint',
48
48
  }),
49
49
  __metadata("design:type", Boolean)
50
50
  ], HttpConfig.prototype, "swaggerUI", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"http.config.js","sourceRoot":"","sources":["../src/http.config.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAAoC;AACpC,qCASoB;AAKb,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,aAAM;IAK3B,IAAI,CAAU;IAOd,IAAI,CAAU;IAMd,SAAS,CAAQ;IAMjB,SAAS,CAAW;CAC9B,CAAA;AAzBY,gCAAU;AAKZ;IAJR,IAAA,eAAQ,EAAC,WAAW,EAAE,IAAI,mBAAY,EAAE,EAAE;QACzC,YAAY,EAAE,SAAS;QACvB,WAAW,EAAE,WAAW;KACzB,CAAC;;wCACqB;AAOd;IALR,IAAA,eAAQ,EAAC,WAAW,EAAE,IAAI,gBAAS,EAAE,EAAE;QACtC,4DAA4D;QAC5D,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,WAAW;KACzB,CAAC;;wCACqB;AAMd;IAJR,IAAA,eAAQ,EAAC,iBAAiB,EAAE,IAAI,iBAAU,EAAE,EAAE;QAC7C,YAAY,EAAE,IAAI,aAAI,CAAC,GAAG,EAAE,IAAI,CAAC;QACjC,WAAW,EAAE,oBAAoB;KAClC,CAAC;8BACmB,aAAI;6CAAC;AAMjB;IAJR,IAAA,eAAQ,EAAC,yBAAyB,EAAE,IAAI,iBAAU,EAAE,EAAE;QACrD,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,wCAAwC;KACtD,CAAC;;6CAC2B;qBAxBlB,UAAU;IAHtB,IAAA,oBAAa,EAAC;QACb,IAAI,EAAE,CAAC,gBAAS,CAAC,cAAc,EAAE,gBAAS,CAAC,SAAS,EAAE,gBAAS,CAAC,IAAI,CAAC;KACtE,CAAC;GACW,UAAU,CAyBtB"}
1
+ {"version":3,"file":"http.config.js","sourceRoot":"","sources":["../src/http.config.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAMsB;AACtB,qCAAwE;AAKjE,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,aAAM;IAK3B,IAAI,CAAU;IAOd,IAAI,CAAU;IAMd,SAAS,CAAQ;IAMjB,SAAS,CAAW;CAC9B,CAAA;AAzBY,gCAAU;AAKZ;IAJR,IAAA,eAAQ,EAAC,WAAW,EAAE,IAAI,qBAAY,EAAE,EAAE;QACzC,YAAY,EAAE,SAAS;QACvB,WAAW,EAAE,WAAW;KACzB,CAAC;;wCACqB;AAOd;IALR,IAAA,eAAQ,EAAC,WAAW,EAAE,IAAI,kBAAS,EAAE,EAAE;QACtC,4DAA4D;QAC5D,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,WAAW;KACzB,CAAC;;wCACqB;AAMd;IAJR,IAAA,eAAQ,EAAC,iBAAiB,EAAE,IAAI,mBAAU,EAAE,EAAE;QAC7C,YAAY,EAAE,IAAI,aAAI,CAAC,GAAG,EAAE,IAAI,CAAC;QACjC,WAAW,EAAE,oBAAoB;KAClC,CAAC;8BACmB,aAAI;6CAAC;AAMjB;IAJR,IAAA,eAAQ,EAAC,yBAAyB,EAAE,IAAI,mBAAU,EAAE,EAAE;QACrD,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,4DAA4D;KAC1E,CAAC;;6CAC2B;qBAxBlB,UAAU;IAHtB,IAAA,oBAAa,EAAC;QACb,IAAI,EAAE,CAAC,gBAAS,CAAC,cAAc,EAAE,gBAAS,CAAC,SAAS,EAAE,gBAAS,CAAC,IAAI,CAAC;KACtE,CAAC;GACW,UAAU,CAyBtB"}
@@ -2,9 +2,10 @@ import { type ExecutionContext } from '@nestjs/common';
2
2
  import type { Controller, INestApplication } from '@nestjs/common/interfaces';
3
3
  import type { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
4
4
  import type { AbstractHttpAdapter } from '@nestjs/core';
5
- import { DocumentBuilder as OriginalDocumentBuilder } from '@nestjs/swagger';
6
5
  import type { Constructor, DeepPartial } from '@rsdk/common';
7
- import type { ConfigContext, GenericHeaders, HttpOptions, IErrorsFormatter, IErrorsSender, IErrorsTransformer, IHttpTransport, LogFormatter } from '@rsdk/core';
6
+ import type { ConfigContext, GenericHeaders, HttpOptions, IErrorsFormatter, IErrorsSender, IErrorsTransformer, IHttpTransport, LogFormatter, PlatformContext } from '@rsdk/core';
7
+ import type { OpenApiOptions } from '@rsdk/http.openapi';
8
+ import type { ILogger } from '@rsdk/logging';
8
9
  import { HttpConfig } from './http.config';
9
10
  export interface ParsersConfig {
10
11
  body: {
@@ -13,12 +14,12 @@ export interface ParsersConfig {
13
14
  };
14
15
  cookie: boolean;
15
16
  }
16
- export type DocumentBuilder = Omit<OriginalDocumentBuilder, 'build' | 'setTitle' | 'setDescription' | 'setVersion'>;
17
- export interface SwaggerOptions {
18
- url: string;
19
- file: string;
20
- configure(builder: DocumentBuilder): void;
21
- }
17
+ export type SwaggerOptions = Partial<OpenApiOptions> | {
18
+ fromFile: string;
19
+ };
20
+ export declare function isFromFile(swagger: SwaggerOptions): swagger is {
21
+ fromFile: string;
22
+ };
22
23
  export interface HttpTransportOptions {
23
24
  /**
24
25
  * CORS middleware options
@@ -30,7 +31,6 @@ export interface HttpTransportOptions {
30
31
  globalPrefix?: string;
31
32
  /**
32
33
  * Configuration of body and cookie parsers
33
- // NOTE: This fields is actually is handled in transport specific way
34
34
  */
35
35
  parsers?: DeepPartial<ParsersConfig>;
36
36
  /**
@@ -40,11 +40,11 @@ export interface HttpTransportOptions {
40
40
  * NOTE: SwaggerUI is enabled or disabled by HTTP_SWAGGER_UI_ENABLED
41
41
  * (true by default).
42
42
  */
43
- swagger?: DeepPartial<SwaggerOptions>;
43
+ swagger?: Record<string, SwaggerOptions>;
44
44
  }
45
45
  export declare abstract class AbstractHttpTransport implements IHttpTransport {
46
- protected readonly options?: HttpTransportOptions | undefined;
47
- constructor(options?: HttpTransportOptions | undefined);
46
+ protected readonly options: HttpTransportOptions;
47
+ constructor(options?: HttpTransportOptions);
48
48
  extractHeaders(ctx: ExecutionContext): GenericHeaders;
49
49
  matchByContext(ctx: ExecutionContext): boolean;
50
50
  errorFormatter(): IErrorsFormatter;
@@ -55,7 +55,7 @@ export declare abstract class AbstractHttpTransport implements IHttpTransport {
55
55
  getMetricsController(): Constructor<Controller>;
56
56
  createHttpOptions(configContext: ConfigContext): HttpOptions;
57
57
  createAdapter(configContext: ConfigContext): AbstractHttpAdapter;
58
- configureApp(app: INestApplication, configContext: ConfigContext): Promise<void> | void;
58
+ configureApp(app: INestApplication, context: PlatformContext, logger: ILogger): Promise<void>;
59
59
  abstract createHttpAdapter(config: HttpConfig): AbstractHttpAdapter;
60
60
  abstract logFormatter(): LogFormatter;
61
61
  }
@@ -1,16 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AbstractHttpTransport = void 0;
4
+ exports.isFromFile = isFromFile;
4
5
  const swagger_1 = require("@nestjs/swagger");
6
+ const common_node_1 = require("@rsdk/common.node");
5
7
  const core_1 = require("@rsdk/core");
6
- const node_fs_1 = require("node:fs");
8
+ const http_openapi_1 = require("@rsdk/http.openapi");
7
9
  const controllers_1 = require("./controllers");
8
10
  const error_handling_1 = require("./error-handling");
9
11
  const http_config_1 = require("./http.config");
10
12
  const http_headers_1 = require("./http.headers");
13
+ function isFromFile(swagger) {
14
+ return typeof swagger === 'object' && 'fromFile' in swagger;
15
+ }
11
16
  class AbstractHttpTransport {
12
17
  options;
13
- constructor(options) {
18
+ constructor(options = {}) {
14
19
  this.options = options;
15
20
  }
16
21
  extractHeaders(ctx) {
@@ -39,38 +44,42 @@ class AbstractHttpTransport {
39
44
  }
40
45
  createHttpOptions(configContext) {
41
46
  const { host, port } = configContext.resolve(http_config_1.HttpConfig);
42
- return {
43
- host,
44
- port,
45
- };
47
+ return { host, port };
46
48
  }
47
49
  createAdapter(configContext) {
48
50
  return this.createHttpAdapter(configContext.resolve(http_config_1.HttpConfig));
49
51
  }
50
- configureApp(app, configContext) {
52
+ async configureApp(app, context, logger) {
53
+ const configContext = await context.getConfigContext();
51
54
  const { swaggerUI } = configContext.resolve(http_config_1.HttpConfig);
52
- const { globalPrefix, swagger, cors } = this.options ?? {};
55
+ const { globalPrefix, swagger, cors } = this.options;
53
56
  if (globalPrefix) {
54
57
  app.setGlobalPrefix(globalPrefix);
55
58
  }
56
59
  if (cors) {
57
60
  app.enableCors(cors);
58
61
  }
59
- if (swaggerUI || swagger?.file) {
60
- const builder = new swagger_1.DocumentBuilder()
61
- .setTitle(app.get(core_1.APP_NAME))
62
- .setVersion(app.get(core_1.APP_VERSION))
63
- .setDescription(app.get(core_1.APP_DESCRIPTION));
64
- // Applies additional configuration from user
65
- swagger?.configure?.(builder);
66
- const openApiConfig = builder.build();
67
- const spec = swagger_1.SwaggerModule.createDocument(app, openApiConfig);
68
- if (swagger?.file) {
69
- (0, node_fs_1.writeFileSync)(swagger.file, JSON.stringify(spec));
70
- }
71
- if (swaggerUI) {
72
- swagger_1.SwaggerModule.setup(swagger?.url ?? '/swagger', app, spec);
62
+ if (!swaggerUI || !swagger) {
63
+ return;
64
+ }
65
+ for (const [url, config] of Object.entries(swagger)) {
66
+ if (isFromFile(config)) {
67
+ try {
68
+ const document = await (0, common_node_1.readObj)(config.fromFile);
69
+ swagger_1.SwaggerModule.setup(url, app, document);
70
+ }
71
+ catch {
72
+ logger.warn(`Failed to load OpenAPI spec from ${config.fromFile}`);
73
+ }
74
+ continue;
73
75
  }
76
+ const document = await new http_openapi_1.OpenApiGenerator(context, {
77
+ title: app.get(core_1.APP_NAME),
78
+ version: app.get(core_1.APP_VERSION),
79
+ description: app.get(core_1.APP_DESCRIPTION),
80
+ ...config,
81
+ }).generate();
82
+ swagger_1.SwaggerModule.setup(url, app, document);
74
83
  }
75
84
  }
76
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"http.transport.js","sourceRoot":"","sources":["../src/http.transport.ts"],"names":[],"mappings":";;;AAIA,6CAGyB;AAYzB,qCAAoE;AACpE,qCAAwC;AAExC,+CAA4E;AAC5E,qDAI0B;AAC1B,+CAA2C;AAC3C,iDAA6C;AAiD7C,MAAsB,qBAAqB;IACV;IAA/B,YAA+B,OAA8B;QAA9B,YAAO,GAAP,OAAO,CAAuB;IAAG,CAAC;IAEjE,cAAc,CAAC,GAAqB;QAClC,OAAO,IAAI,0BAAW,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,cAAc,CAAC,GAAqB;QAClC,OAAO,GAAG,CAAC,OAAO,EAAE,KAAK,MAAM,CAAC;IAClC,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,oCAAmB,EAAE,CAAC;IACnC,CAAC;IAED,WAAW;QACT,OAAO,IAAI,iCAAgB,EAAE,CAAC;IAChC,CAAC;IAED,iBAAiB;QACf,OAAO,CAAC,IAAI,sCAAqB,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,WAAW;QACT,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,mBAAmB;QACjB,OAAO,kCAAoB,CAAC;IAC9B,CAAC;IAED,oBAAoB;QAClB,OAAO,mCAAqB,CAAC;IAC/B,CAAC;IAED,iBAAiB,CAAC,aAA4B;QAC5C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,wBAAU,CAAC,CAAC;QAEzD,OAAO;YACL,IAAI;YACJ,IAAI;SACL,CAAC;IACJ,CAAC;IAED,aAAa,CAAC,aAA4B;QACxC,OAAO,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,OAAO,CAAC,wBAAU,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,YAAY,CACV,GAAqB,EACrB,aAA4B;QAE5B,MAAM,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,wBAAU,CAAC,CAAC;QACxD,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;QAC3D,IAAI,YAAY,EAAE,CAAC;YACjB,GAAG,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,IAAI,EAAE,CAAC;YACT,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAED,IAAI,SAAS,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,IAAI,yBAAuB,EAAE;iBAC1C,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,eAAQ,CAAC,CAAC;iBAC3B,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,kBAAW,CAAC,CAAC;iBAChC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,sBAAe,CAAC,CAAC,CAAC;YAE5C,6CAA6C;YAC7C,OAAO,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC;YAE9B,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;YAE9D,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;gBAClB,IAAA,uBAAa,EAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YACpD,CAAC;YAED,IAAI,SAAS,EAAE,CAAC;gBACd,uBAAa,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;CAKF;AAtFD,sDAsFC"}
1
+ {"version":3,"file":"http.transport.js","sourceRoot":"","sources":["../src/http.transport.ts"],"names":[],"mappings":";;;AA4CA,gCAIC;AA3CD,6CAAgD;AAEhD,mDAA4C;AAY5C,qCAAoE;AAEpE,qDAAsD;AAItD,+CAA4E;AAC5E,qDAI0B;AAC1B,+CAA2C;AAC3C,iDAA6C;AAY7C,SAAgB,UAAU,CACxB,OAAuB;IAEvB,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,UAAU,IAAI,OAAO,CAAC;AAC9D,CAAC;AA4BD,MAAsB,qBAAqB;IACV;IAA/B,YAA+B,UAAgC,EAAE;QAAlC,YAAO,GAAP,OAAO,CAA2B;IAAG,CAAC;IAErE,cAAc,CAAC,GAAqB;QAClC,OAAO,IAAI,0BAAW,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,cAAc,CAAC,GAAqB;QAClC,OAAO,GAAG,CAAC,OAAO,EAAE,KAAK,MAAM,CAAC;IAClC,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,oCAAmB,EAAE,CAAC;IACnC,CAAC;IAED,WAAW;QACT,OAAO,IAAI,iCAAgB,EAAE,CAAC;IAChC,CAAC;IAED,iBAAiB;QACf,OAAO,CAAC,IAAI,sCAAqB,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,WAAW;QACT,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,mBAAmB;QACjB,OAAO,kCAAoB,CAAC;IAC9B,CAAC;IAED,oBAAoB;QAClB,OAAO,mCAAqB,CAAC;IAC/B,CAAC;IAED,iBAAiB,CAAC,aAA4B;QAC5C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,wBAAU,CAAC,CAAC;QAEzD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,aAAa,CAAC,aAA4B;QACxC,OAAO,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,OAAO,CAAC,wBAAU,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,GAAqB,EACrB,OAAwB,EACxB,MAAe;QAEf,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACvD,MAAM,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,wBAAU,CAAC,CAAC;QACxD,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAErD,IAAI,YAAY,EAAE,CAAC;YACjB,GAAG,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QACpC,CAAC;QAED,IAAI,IAAI,EAAE,CAAC;YACT,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAED,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACpD,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC;oBACH,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAO,EAAgB,MAAM,CAAC,QAAQ,CAAC,CAAC;oBAE/D,uBAAa,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAC1C,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,CAAC,IAAI,CAAC,oCAAoC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACrE,CAAC;gBAED,SAAS;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,IAAI,+BAAgB,CAAC,OAAO,EAAE;gBACnD,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,eAAQ,CAAC;gBACxB,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,kBAAW,CAAC;gBAC7B,WAAW,EAAE,GAAG,CAAC,GAAG,CAAC,sBAAe,CAAC;gBACrC,GAAG,MAAM;aACV,CAAC,CAAC,QAAQ,EAAE,CAAC;YAEd,uBAAa,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;CAKF;AA7FD,sDA6FC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsdk/http.server",
3
- "version": "6.0.0-next.1",
3
+ "version": "6.0.0-next.10",
4
4
  "description": "HTTP transport for rsdk apps (needs some of HTTP adapters)",
5
5
  "license": "Apache License 2.0",
6
6
  "publishConfig": {
@@ -15,13 +15,16 @@
15
15
  "@nestjs/core": "^10.0.0",
16
16
  "@nestjs/swagger": "^7.0.0 || ^8.0.0",
17
17
  "@rsdk/common": "*",
18
+ "@rsdk/common.node": "*",
18
19
  "@rsdk/core": "*",
20
+ "@rsdk/http.openapi": "*",
19
21
  "@rsdk/logging": "*",
20
22
  "@rsdk/zones": "*",
21
23
  "body-parser": "^1.20.1",
22
24
  "cookie-parser": "^1.4.6",
25
+ "lodash": "^4.17.21",
23
26
  "reflect-metadata": "^0.1.12 || ^0.2.0",
24
27
  "rxjs": "^7.8.1"
25
28
  },
26
- "gitHead": "3114a76df17cf98d6f3cbb8a931599a883af6783"
29
+ "gitHead": "26c1a04480e6209098be6636afbde7fd357da61a"
27
30
  }
@@ -1,14 +1,11 @@
1
- import { Size } from '@rsdk/common';
2
1
  import {
3
2
  BoolParser,
4
- Config,
5
- ConfigSection,
6
- ConfigTag,
7
3
  IntParser,
8
- Property,
4
+ Size,
9
5
  SizeParser,
10
6
  StringParser,
11
- } from '@rsdk/core';
7
+ } from '@rsdk/common';
8
+ import { Config, ConfigSection, ConfigTag, Property } from '@rsdk/core';
12
9
 
13
10
  @ConfigSection({
14
11
  tags: [ConfigTag.infrastructure, ConfigTag.transport, ConfigTag.http],
@@ -35,7 +32,7 @@ export class HttpConfig extends Config {
35
32
 
36
33
  @Property('HTTP_SWAGGER_UI_ENABLED', new BoolParser(), {
37
34
  defaultValue: true,
38
- description: 'Enable swagger UI on /swagger endpoint',
35
+ description: 'Enable swagger UI on /swagger (can be configured) endpoint',
39
36
  })
40
37
  readonly swaggerUI!: boolean;
41
38
  }
@@ -2,11 +2,10 @@ import { type ExecutionContext } from '@nestjs/common';
2
2
  import type { Controller, INestApplication } from '@nestjs/common/interfaces';
3
3
  import type { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
4
4
  import type { AbstractHttpAdapter } from '@nestjs/core';
5
- import {
6
- DocumentBuilder as OriginalDocumentBuilder,
7
- SwaggerModule,
8
- } from '@nestjs/swagger';
5
+ import type { OpenAPIObject } from '@nestjs/swagger';
6
+ import { SwaggerModule } from '@nestjs/swagger';
9
7
  import type { Constructor, DeepPartial } from '@rsdk/common';
8
+ import { readObj } from '@rsdk/common.node';
10
9
  import type {
11
10
  ConfigContext,
12
11
  GenericHeaders,
@@ -16,9 +15,13 @@ import type {
16
15
  IErrorsTransformer,
17
16
  IHttpTransport,
18
17
  LogFormatter,
18
+ PlatformContext,
19
19
  } from '@rsdk/core';
20
20
  import { APP_DESCRIPTION, APP_NAME, APP_VERSION } from '@rsdk/core';
21
- import { writeFileSync } from 'node:fs';
21
+ import type { OpenApiOptions } from '@rsdk/http.openapi';
22
+ import { OpenApiGenerator } from '@rsdk/http.openapi';
23
+ import type { ILogger } from '@rsdk/logging';
24
+ import _ from 'lodash';
22
25
 
23
26
  import { HealthHttpController, MetricsHttpController } from './controllers';
24
27
  import {
@@ -37,16 +40,12 @@ export interface ParsersConfig {
37
40
  cookie: boolean;
38
41
  }
39
42
 
40
- export type DocumentBuilder = Omit<
41
- OriginalDocumentBuilder,
42
- 'build' | 'setTitle' | 'setDescription' | 'setVersion'
43
- >;
44
-
45
- export interface SwaggerOptions {
46
- url: string;
47
- file: string;
43
+ export type SwaggerOptions = Partial<OpenApiOptions> | { fromFile: string };
48
44
 
49
- configure(builder: DocumentBuilder): void;
45
+ export function isFromFile(
46
+ swagger: SwaggerOptions,
47
+ ): swagger is { fromFile: string } {
48
+ return typeof swagger === 'object' && 'fromFile' in swagger;
50
49
  }
51
50
 
52
51
  export interface HttpTransportOptions {
@@ -62,7 +61,6 @@ export interface HttpTransportOptions {
62
61
 
63
62
  /**
64
63
  * Configuration of body and cookie parsers
65
- // NOTE: This fields is actually is handled in transport specific way
66
64
  */
67
65
  parsers?: DeepPartial<ParsersConfig>;
68
66
 
@@ -73,11 +71,11 @@ export interface HttpTransportOptions {
73
71
  * NOTE: SwaggerUI is enabled or disabled by HTTP_SWAGGER_UI_ENABLED
74
72
  * (true by default).
75
73
  */
76
- swagger?: DeepPartial<SwaggerOptions>;
74
+ swagger?: Record<string, SwaggerOptions>;
77
75
  }
78
76
 
79
77
  export abstract class AbstractHttpTransport implements IHttpTransport {
80
- constructor(protected readonly options?: HttpTransportOptions) {}
78
+ constructor(protected readonly options: HttpTransportOptions = {}) {}
81
79
 
82
80
  extractHeaders(ctx: ExecutionContext): GenericHeaders {
83
81
  return new HttpHeaders(ctx);
@@ -114,48 +112,55 @@ export abstract class AbstractHttpTransport implements IHttpTransport {
114
112
  createHttpOptions(configContext: ConfigContext): HttpOptions {
115
113
  const { host, port } = configContext.resolve(HttpConfig);
116
114
 
117
- return {
118
- host,
119
- port,
120
- };
115
+ return { host, port };
121
116
  }
122
117
 
123
118
  createAdapter(configContext: ConfigContext): AbstractHttpAdapter {
124
119
  return this.createHttpAdapter(configContext.resolve(HttpConfig));
125
120
  }
126
121
 
127
- configureApp(
122
+ async configureApp(
128
123
  app: INestApplication,
129
- configContext: ConfigContext,
130
- ): Promise<void> | void {
124
+ context: PlatformContext,
125
+ logger: ILogger,
126
+ ): Promise<void> {
127
+ const configContext = await context.getConfigContext();
131
128
  const { swaggerUI } = configContext.resolve(HttpConfig);
132
- const { globalPrefix, swagger, cors } = this.options ?? {};
129
+ const { globalPrefix, swagger, cors } = this.options;
130
+
133
131
  if (globalPrefix) {
134
132
  app.setGlobalPrefix(globalPrefix);
135
133
  }
134
+
136
135
  if (cors) {
137
136
  app.enableCors(cors);
138
137
  }
139
138
 
140
- if (swaggerUI || swagger?.file) {
141
- const builder = new OriginalDocumentBuilder()
142
- .setTitle(app.get(APP_NAME))
143
- .setVersion(app.get(APP_VERSION))
144
- .setDescription(app.get(APP_DESCRIPTION));
139
+ if (!swaggerUI || !swagger) {
140
+ return;
141
+ }
145
142
 
146
- // Applies additional configuration from user
147
- swagger?.configure?.(builder);
143
+ for (const [url, config] of Object.entries(swagger)) {
144
+ if (isFromFile(config)) {
145
+ try {
146
+ const document = await readObj<OpenAPIObject>(config.fromFile);
148
147
 
149
- const openApiConfig = builder.build();
150
- const spec = SwaggerModule.createDocument(app, openApiConfig);
148
+ SwaggerModule.setup(url, app, document);
149
+ } catch {
150
+ logger.warn(`Failed to load OpenAPI spec from ${config.fromFile}`);
151
+ }
151
152
 
152
- if (swagger?.file) {
153
- writeFileSync(swagger.file, JSON.stringify(spec));
153
+ continue;
154
154
  }
155
155
 
156
- if (swaggerUI) {
157
- SwaggerModule.setup(swagger?.url ?? '/swagger', app, spec);
158
- }
156
+ const document = await new OpenApiGenerator(context, {
157
+ title: app.get(APP_NAME),
158
+ version: app.get(APP_VERSION),
159
+ description: app.get(APP_DESCRIPTION),
160
+ ...config,
161
+ }).generate();
162
+
163
+ SwaggerModule.setup(url, app, document);
159
164
  }
160
165
  }
161
166