@opra/nestjs 0.25.4 → 0.26.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.
@@ -25,67 +25,75 @@ let OpraApiFactory = exports.OpraApiFactory = class OpraApiFactory {
25
25
  const info = { title: '', version: '', ...moduleOptions.info };
26
26
  info.title = info.title || 'Untitled service';
27
27
  info.version = info.version || '1';
28
- const resources = [];
28
+ const root = {
29
+ resources: []
30
+ };
29
31
  const apiSchema = {
30
32
  version: common_2.OpraSchema.SpecVersion,
31
33
  info,
32
34
  types: [],
33
- resources
35
+ root
34
36
  };
35
37
  /*
36
38
  * Walk through modules and add Resource instances to the api schema
37
39
  */
38
- const wrappers = this.explorerService.exploreSourceWrappers(rootModule);
39
- for (const wrapper of wrappers) {
40
+ this.explorerService.exploreResources(rootModule, (wrapper, modulePath) => {
40
41
  const instance = wrapper.instance;
41
42
  const ctor = instance.constructor;
42
43
  const metadata = Reflect.getMetadata(common_2.RESOURCE_METADATA, ctor);
43
- if (common_2.OpraSchema.isSource(metadata))
44
- resources.push(instance);
45
- }
46
- for (const wrapper of wrappers) {
47
- const instance = wrapper.instance;
48
- const ctor = instance.constructor;
49
- const sourceDef = Reflect.getMetadata(common_2.RESOURCE_METADATA, ctor);
50
- /* istanbul ignore next */
51
- if (!sourceDef)
52
- continue;
53
- resources.push(instance);
54
- /* Wrap resolver functions */
55
- const isRequestScoped = !wrapper.isDependencyTreeStatic();
56
- if (sourceDef.operations &&
57
- (common_2.OpraSchema.isCollection(sourceDef) || common_2.OpraSchema.isSingleton(sourceDef) || common_2.OpraSchema.isStorage(sourceDef))) {
58
- for (const methodName of Object.keys(sourceDef.operations)) {
59
- const endpointFunction = instance[methodName];
60
- const nestHandlerName = methodName + '_nestjs';
61
- // Skip patch if controller do not have function for endpoint or already patched before
62
- if (typeof endpointFunction !== 'function')
63
- continue;
64
- // NestJs requires calling handler function in different order than Opra.
65
- // In NestJS, handler functions must be called with these parameters (req, res, next)
66
- // In Opra, handler functions must be called with these parameters (context)
67
- // To work handlers properly we create new handlers that will work as a proxy to wrap parameters
68
- // Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
69
- const paramArgsMetadata = Reflect.getMetadata(constants_js_1.PARAM_ARGS_METADATA, instance.constructor, methodName);
70
- const hasParamsArgs = !!paramArgsMetadata;
71
- const patchedFn = instance[nestHandlerName] = function (...args) {
72
- if (hasParamsArgs)
73
- return endpointFunction.apply(this, args);
74
- return endpointFunction.call(this, args[3]);
75
- };
76
- if (paramArgsMetadata)
77
- Reflect.defineMetadata(constants_js_1.PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
78
- // Copy all metadata from old Function to new one
79
- Reflect.getMetadataKeys(endpointFunction).forEach(k => {
80
- const m = Reflect.getMetadata(k, endpointFunction);
81
- Reflect.defineMetadata(k, m, patchedFn);
82
- });
83
- this._createContextCallback(instance, wrapper, rootModule, methodName, isRequestScoped, contextType);
44
+ let node = root;
45
+ modulePath.forEach(m => {
46
+ const mt = Reflect.getMetadata(common_2.RESOURCE_METADATA, m._metatype);
47
+ if (mt) {
48
+ let n = node.resources.find(x => x.controller === m.instance);
49
+ if (!n) {
50
+ n = {
51
+ ...mt,
52
+ kind: 'Container',
53
+ resources: [...(mt.resources || [])],
54
+ controller: m.instance
55
+ };
56
+ node.resources.push(n);
57
+ }
58
+ node = n;
84
59
  }
60
+ });
61
+ // Do not add Modules decorated with @Container
62
+ if (wrapper.metatype !== wrapper.host?.metatype)
63
+ node.resources.push(instance);
64
+ /* Wrap operation and action functions */
65
+ const isRequestScoped = !wrapper.isDependencyTreeStatic();
66
+ const methodNames = [...Object.keys(metadata.operations || []), ...Object.keys(metadata.actions || [])];
67
+ for (const methodName of methodNames) {
68
+ const endpointFunction = instance[methodName];
69
+ const nestHandlerName = methodName + '_nestjs';
70
+ // Skip patch if controller do not have function for endpoint or already patched before
71
+ if (typeof endpointFunction !== 'function')
72
+ continue;
73
+ // NestJs requires calling handler function in different order than Opra.
74
+ // In NestJS, handler functions must be called with these parameters (req, res, next)
75
+ // In Opra, handler functions must be called with these parameters (context)
76
+ // To work handlers properly we create new handlers that will work as a proxy to wrap parameters
77
+ // Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
78
+ const paramArgsMetadata = Reflect.getMetadata(constants_js_1.PARAM_ARGS_METADATA, instance.constructor, methodName);
79
+ const hasParamsArgs = !!paramArgsMetadata;
80
+ const patchedFn = instance[nestHandlerName] = function (...args) {
81
+ if (hasParamsArgs)
82
+ return endpointFunction.apply(this, args);
83
+ return endpointFunction.call(this, args[3]);
84
+ };
85
+ if (paramArgsMetadata)
86
+ Reflect.defineMetadata(constants_js_1.PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
87
+ // Copy all metadata from old Function to new one
88
+ Reflect.getMetadataKeys(endpointFunction).forEach(k => {
89
+ const m = Reflect.getMetadata(k, endpointFunction);
90
+ Reflect.defineMetadata(k, m, patchedFn);
91
+ });
92
+ this._createContextCallback(instance, wrapper, rootModule, methodName, isRequestScoped, contextType);
85
93
  }
86
- }
94
+ });
87
95
  // Create api document
88
- return common_2.DocumentFactory.createDocument(apiSchema);
96
+ return common_2.ApiDocumentFactory.createDocument(apiSchema);
89
97
  }
90
98
  _createHandler(callback) {
91
99
  return function (ctx) {
@@ -3,32 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NestExplorer = void 0;
4
4
  const common_1 = require("@opra/common");
5
5
  class NestExplorer {
6
- exploreProviders(rootModule, predicate) {
6
+ exploreProviders(rootModule, callback) {
7
7
  const modules = new Set();
8
- const wrappers = new Set();
8
+ const tree = [];
9
9
  const scanModules = (m) => {
10
10
  if (modules.has(m))
11
11
  return;
12
12
  modules.add(m);
13
+ tree.push(m);
13
14
  for (const mm of m.imports.values()) {
14
15
  scanModules(mm);
15
16
  }
16
17
  for (const wrapper of m.providers.values()) {
17
- if (!wrappers.has(wrapper) && predicate(wrapper))
18
- wrappers.add(wrapper);
18
+ callback(wrapper, tree);
19
19
  if (wrapper.host)
20
20
  scanModules(wrapper.host);
21
21
  }
22
+ tree.pop();
22
23
  };
23
24
  scanModules(rootModule);
24
- return Array.from(wrappers.values());
25
25
  }
26
- exploreSourceWrappers(rootModule) {
27
- return this.exploreProviders(rootModule, (wrapper) => {
28
- return !!(wrapper.instance
26
+ exploreResources(rootModule, callback) {
27
+ this.exploreProviders(rootModule, (wrapper, tree) => {
28
+ if (wrapper.instance
29
29
  && typeof wrapper.instance === 'object'
30
30
  && wrapper.instance.constructor
31
- && common_1.OpraSchema.isSource(Reflect.getMetadata(common_1.RESOURCE_METADATA, wrapper.instance.constructor)));
31
+ && common_1.OpraSchema.isResource(Reflect.getMetadata(common_1.RESOURCE_METADATA, wrapper.instance.constructor))) {
32
+ callback(wrapper, tree);
33
+ }
34
+ return false;
32
35
  });
33
36
  }
34
37
  }
@@ -30,7 +30,7 @@ class OpraApiLoader {
30
30
  };
31
31
  try {
32
32
  const apiDocument = await this.opraFactory.generateService(rootModule, options, 'http');
33
- if (!apiDocument.resources.size) {
33
+ if (!Object.keys(apiDocument.root).length) {
34
34
  this.logger.warn(`No Sources found (${name})`);
35
35
  return;
36
36
  }
@@ -6,7 +6,7 @@ import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-cr
6
6
  import { Injector } from '@nestjs/core/injector/injector';
7
7
  import { InternalCoreModule } from '@nestjs/core/injector/internal-core-module';
8
8
  import { REQUEST_CONTEXT_ID } from '@nestjs/core/router/request/request-constants';
9
- import { DocumentFactory, OpraSchema, RESOURCE_METADATA } from '@opra/common';
9
+ import { ApiDocumentFactory, OpraSchema, RESOURCE_METADATA } from '@opra/common';
10
10
  import { PARAM_ARGS_METADATA } from '../constants.js';
11
11
  import { HandlerParamType } from '../enums/handler-paramtype.enum.js';
12
12
  import { NestExplorer } from '../services/nest-explorer.js';
@@ -22,67 +22,75 @@ export let OpraApiFactory = class OpraApiFactory {
22
22
  const info = { title: '', version: '', ...moduleOptions.info };
23
23
  info.title = info.title || 'Untitled service';
24
24
  info.version = info.version || '1';
25
- const resources = [];
25
+ const root = {
26
+ resources: []
27
+ };
26
28
  const apiSchema = {
27
29
  version: OpraSchema.SpecVersion,
28
30
  info,
29
31
  types: [],
30
- resources
32
+ root
31
33
  };
32
34
  /*
33
35
  * Walk through modules and add Resource instances to the api schema
34
36
  */
35
- const wrappers = this.explorerService.exploreSourceWrappers(rootModule);
36
- for (const wrapper of wrappers) {
37
+ this.explorerService.exploreResources(rootModule, (wrapper, modulePath) => {
37
38
  const instance = wrapper.instance;
38
39
  const ctor = instance.constructor;
39
40
  const metadata = Reflect.getMetadata(RESOURCE_METADATA, ctor);
40
- if (OpraSchema.isSource(metadata))
41
- resources.push(instance);
42
- }
43
- for (const wrapper of wrappers) {
44
- const instance = wrapper.instance;
45
- const ctor = instance.constructor;
46
- const sourceDef = Reflect.getMetadata(RESOURCE_METADATA, ctor);
47
- /* istanbul ignore next */
48
- if (!sourceDef)
49
- continue;
50
- resources.push(instance);
51
- /* Wrap resolver functions */
52
- const isRequestScoped = !wrapper.isDependencyTreeStatic();
53
- if (sourceDef.operations &&
54
- (OpraSchema.isCollection(sourceDef) || OpraSchema.isSingleton(sourceDef) || OpraSchema.isStorage(sourceDef))) {
55
- for (const methodName of Object.keys(sourceDef.operations)) {
56
- const endpointFunction = instance[methodName];
57
- const nestHandlerName = methodName + '_nestjs';
58
- // Skip patch if controller do not have function for endpoint or already patched before
59
- if (typeof endpointFunction !== 'function')
60
- continue;
61
- // NestJs requires calling handler function in different order than Opra.
62
- // In NestJS, handler functions must be called with these parameters (req, res, next)
63
- // In Opra, handler functions must be called with these parameters (context)
64
- // To work handlers properly we create new handlers that will work as a proxy to wrap parameters
65
- // Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
66
- const paramArgsMetadata = Reflect.getMetadata(PARAM_ARGS_METADATA, instance.constructor, methodName);
67
- const hasParamsArgs = !!paramArgsMetadata;
68
- const patchedFn = instance[nestHandlerName] = function (...args) {
69
- if (hasParamsArgs)
70
- return endpointFunction.apply(this, args);
71
- return endpointFunction.call(this, args[3]);
72
- };
73
- if (paramArgsMetadata)
74
- Reflect.defineMetadata(PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
75
- // Copy all metadata from old Function to new one
76
- Reflect.getMetadataKeys(endpointFunction).forEach(k => {
77
- const m = Reflect.getMetadata(k, endpointFunction);
78
- Reflect.defineMetadata(k, m, patchedFn);
79
- });
80
- this._createContextCallback(instance, wrapper, rootModule, methodName, isRequestScoped, contextType);
41
+ let node = root;
42
+ modulePath.forEach(m => {
43
+ const mt = Reflect.getMetadata(RESOURCE_METADATA, m._metatype);
44
+ if (mt) {
45
+ let n = node.resources.find(x => x.controller === m.instance);
46
+ if (!n) {
47
+ n = {
48
+ ...mt,
49
+ kind: 'Container',
50
+ resources: [...(mt.resources || [])],
51
+ controller: m.instance
52
+ };
53
+ node.resources.push(n);
54
+ }
55
+ node = n;
81
56
  }
57
+ });
58
+ // Do not add Modules decorated with @Container
59
+ if (wrapper.metatype !== wrapper.host?.metatype)
60
+ node.resources.push(instance);
61
+ /* Wrap operation and action functions */
62
+ const isRequestScoped = !wrapper.isDependencyTreeStatic();
63
+ const methodNames = [...Object.keys(metadata.operations || []), ...Object.keys(metadata.actions || [])];
64
+ for (const methodName of methodNames) {
65
+ const endpointFunction = instance[methodName];
66
+ const nestHandlerName = methodName + '_nestjs';
67
+ // Skip patch if controller do not have function for endpoint or already patched before
68
+ if (typeof endpointFunction !== 'function')
69
+ continue;
70
+ // NestJs requires calling handler function in different order than Opra.
71
+ // In NestJS, handler functions must be called with these parameters (req, res, next)
72
+ // In Opra, handler functions must be called with these parameters (context)
73
+ // To work handlers properly we create new handlers that will work as a proxy to wrap parameters
74
+ // Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
75
+ const paramArgsMetadata = Reflect.getMetadata(PARAM_ARGS_METADATA, instance.constructor, methodName);
76
+ const hasParamsArgs = !!paramArgsMetadata;
77
+ const patchedFn = instance[nestHandlerName] = function (...args) {
78
+ if (hasParamsArgs)
79
+ return endpointFunction.apply(this, args);
80
+ return endpointFunction.call(this, args[3]);
81
+ };
82
+ if (paramArgsMetadata)
83
+ Reflect.defineMetadata(PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
84
+ // Copy all metadata from old Function to new one
85
+ Reflect.getMetadataKeys(endpointFunction).forEach(k => {
86
+ const m = Reflect.getMetadata(k, endpointFunction);
87
+ Reflect.defineMetadata(k, m, patchedFn);
88
+ });
89
+ this._createContextCallback(instance, wrapper, rootModule, methodName, isRequestScoped, contextType);
82
90
  }
83
- }
91
+ });
84
92
  // Create api document
85
- return DocumentFactory.createDocument(apiSchema);
93
+ return ApiDocumentFactory.createDocument(apiSchema);
86
94
  }
87
95
  _createHandler(callback) {
88
96
  return function (ctx) {
@@ -1,31 +1,34 @@
1
1
  import { OpraSchema, RESOURCE_METADATA } from '@opra/common';
2
2
  export class NestExplorer {
3
- exploreProviders(rootModule, predicate) {
3
+ exploreProviders(rootModule, callback) {
4
4
  const modules = new Set();
5
- const wrappers = new Set();
5
+ const tree = [];
6
6
  const scanModules = (m) => {
7
7
  if (modules.has(m))
8
8
  return;
9
9
  modules.add(m);
10
+ tree.push(m);
10
11
  for (const mm of m.imports.values()) {
11
12
  scanModules(mm);
12
13
  }
13
14
  for (const wrapper of m.providers.values()) {
14
- if (!wrappers.has(wrapper) && predicate(wrapper))
15
- wrappers.add(wrapper);
15
+ callback(wrapper, tree);
16
16
  if (wrapper.host)
17
17
  scanModules(wrapper.host);
18
18
  }
19
+ tree.pop();
19
20
  };
20
21
  scanModules(rootModule);
21
- return Array.from(wrappers.values());
22
22
  }
23
- exploreSourceWrappers(rootModule) {
24
- return this.exploreProviders(rootModule, (wrapper) => {
25
- return !!(wrapper.instance
23
+ exploreResources(rootModule, callback) {
24
+ this.exploreProviders(rootModule, (wrapper, tree) => {
25
+ if (wrapper.instance
26
26
  && typeof wrapper.instance === 'object'
27
27
  && wrapper.instance.constructor
28
- && OpraSchema.isSource(Reflect.getMetadata(RESOURCE_METADATA, wrapper.instance.constructor)));
28
+ && OpraSchema.isResource(Reflect.getMetadata(RESOURCE_METADATA, wrapper.instance.constructor))) {
29
+ callback(wrapper, tree);
30
+ }
31
+ return false;
29
32
  });
30
33
  }
31
34
  }
@@ -27,7 +27,7 @@ export class OpraApiLoader {
27
27
  };
28
28
  try {
29
29
  const apiDocument = await this.opraFactory.generateService(rootModule, options, 'http');
30
- if (!apiDocument.resources.size) {
30
+ if (!Object.keys(apiDocument.root).length) {
31
31
  this.logger.warn(`No Sources found (${name})`);
32
32
  return;
33
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/nestjs",
3
- "version": "0.25.4",
3
+ "version": "0.26.0",
4
4
  "description": "Opra NestJS module",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -25,8 +25,8 @@
25
25
  "clean:cover": "rimraf ../../coverage/nestjs"
26
26
  },
27
27
  "dependencies": {
28
- "@opra/common": "^0.25.4",
29
- "@opra/core": "^0.25.4",
28
+ "@opra/common": "^0.26.0",
29
+ "@opra/core": "^0.26.0",
30
30
  "fast-tokenizer": "^1.2.2",
31
31
  "lodash.head": "^4.0.1",
32
32
  "lodash.identity": "^3.0.0",
@@ -40,7 +40,7 @@
40
40
  "@nestjs/testing": "^10.2.4",
41
41
  "@types/lodash": "^4.14.197",
42
42
  "filedirname": "^2.7.0",
43
- "ts-gems": "^2.4.1"
43
+ "ts-gems": "^2.5.0"
44
44
  },
45
45
  "type": "module",
46
46
  "module": "./esm/index.js",
@@ -1,7 +1,7 @@
1
1
  import { ModuleMetadata, Type } from '@nestjs/common';
2
2
  import { OpraSchema } from '@opra/common';
3
- import { HttpAdapter } from '@opra/core';
4
- export type OpraModuleOptions = HttpAdapter.Options & {
3
+ import { NodeHttpAdapter } from '@opra/core';
4
+ export type OpraModuleOptions = NodeHttpAdapter.Options & {
5
5
  id?: any;
6
6
  info?: OpraSchema.DocumentInfo;
7
7
  /**
@@ -1,6 +1,8 @@
1
1
  import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
2
2
  import { Module } from '@nestjs/core/injector/module';
3
+ type WrapperCallback = (wrapper: InstanceWrapper, tree: Module[]) => void;
3
4
  export declare class NestExplorer {
4
- exploreProviders(rootModule: Module, predicate: (wrapper: InstanceWrapper) => boolean): InstanceWrapper<any>[];
5
- exploreSourceWrappers(rootModule: Module): InstanceWrapper[];
5
+ exploreProviders(rootModule: Module, callback: WrapperCallback): void;
6
+ exploreResources(rootModule: Module, callback: WrapperCallback): void;
6
7
  }
8
+ export {};