@nmshd/typescript-rest 3.0.5

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.
Files changed (62) hide show
  1. package/.ci/runChecks.sh +7 -0
  2. package/.eslintrc.js +226 -0
  3. package/.github/dependabot.yml +27 -0
  4. package/.github/workflows/publish.yml +33 -0
  5. package/.github/workflows/test.yml +31 -0
  6. package/.prettierrc +27 -0
  7. package/.vscode/settings.json +31 -0
  8. package/LICENSE +22 -0
  9. package/README.md +128 -0
  10. package/dist/decorators/methods.d.ts +165 -0
  11. package/dist/decorators/methods.js +263 -0
  12. package/dist/decorators/methods.js.map +1 -0
  13. package/dist/decorators/parameters.d.ts +295 -0
  14. package/dist/decorators/parameters.js +423 -0
  15. package/dist/decorators/parameters.js.map +1 -0
  16. package/dist/decorators/services.d.ts +199 -0
  17. package/dist/decorators/services.js +367 -0
  18. package/dist/decorators/services.js.map +1 -0
  19. package/dist/server/config.d.ts +4 -0
  20. package/dist/server/config.js +43 -0
  21. package/dist/server/config.js.map +1 -0
  22. package/dist/server/model/errors.d.ts +111 -0
  23. package/dist/server/model/errors.js +178 -0
  24. package/dist/server/model/errors.js.map +1 -0
  25. package/dist/server/model/metadata.d.ts +91 -0
  26. package/dist/server/model/metadata.js +80 -0
  27. package/dist/server/model/metadata.js.map +1 -0
  28. package/dist/server/model/return-types.d.ts +86 -0
  29. package/dist/server/model/return-types.js +110 -0
  30. package/dist/server/model/return-types.js.map +1 -0
  31. package/dist/server/model/server-types.d.ts +118 -0
  32. package/dist/server/model/server-types.js +47 -0
  33. package/dist/server/model/server-types.js.map +1 -0
  34. package/dist/server/parameter-processor.d.ts +13 -0
  35. package/dist/server/parameter-processor.js +84 -0
  36. package/dist/server/parameter-processor.js.map +1 -0
  37. package/dist/server/server-container.d.ts +55 -0
  38. package/dist/server/server-container.js +432 -0
  39. package/dist/server/server-container.js.map +1 -0
  40. package/dist/server/server.d.ts +136 -0
  41. package/dist/server/server.js +278 -0
  42. package/dist/server/server.js.map +1 -0
  43. package/dist/server/service-invoker.d.ts +28 -0
  44. package/dist/server/service-invoker.js +270 -0
  45. package/dist/server/service-invoker.js.map +1 -0
  46. package/dist/typescript-rest.d.ts +9 -0
  47. package/dist/typescript-rest.js +31 -0
  48. package/dist/typescript-rest.js.map +1 -0
  49. package/package.json +113 -0
  50. package/src/decorators/methods.ts +279 -0
  51. package/src/decorators/parameters.ts +442 -0
  52. package/src/decorators/services.ts +390 -0
  53. package/src/server/config.ts +40 -0
  54. package/src/server/model/errors.ts +178 -0
  55. package/src/server/model/metadata.ts +118 -0
  56. package/src/server/model/return-types.ts +109 -0
  57. package/src/server/model/server-types.ts +130 -0
  58. package/src/server/parameter-processor.ts +105 -0
  59. package/src/server/server-container.ts +504 -0
  60. package/src/server/server.ts +340 -0
  61. package/src/server/service-invoker.ts +266 -0
  62. package/src/typescript-rest.ts +16 -0
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParserType = exports.ReferencedResource = exports.ServiceContext = exports.HttpMethod = void 0;
4
+ /**
5
+ * The supported HTTP methods.
6
+ */
7
+ var HttpMethod;
8
+ (function (HttpMethod) {
9
+ HttpMethod["GET"] = "GET";
10
+ HttpMethod["POST"] = "POST";
11
+ HttpMethod["PUT"] = "PUT";
12
+ HttpMethod["DELETE"] = "DELETE";
13
+ HttpMethod["HEAD"] = "HEAD";
14
+ HttpMethod["OPTIONS"] = "OPTIONS";
15
+ HttpMethod["PATCH"] = "PATCH";
16
+ })(HttpMethod || (exports.HttpMethod = HttpMethod = {}));
17
+ /**
18
+ * Represents the current context of the request being handled.
19
+ */
20
+ class ServiceContext {
21
+ }
22
+ exports.ServiceContext = ServiceContext;
23
+ /**
24
+ * Used to create a reference to a resource.
25
+ */
26
+ class ReferencedResource {
27
+ /**
28
+ * Constructor. Receives the location of the resource.
29
+ * @param location To be added to the Location header on response
30
+ * @param statusCode the response status code to be sent
31
+ */
32
+ constructor(location, statusCode) {
33
+ this.location = location;
34
+ this.statusCode = statusCode;
35
+ }
36
+ }
37
+ exports.ReferencedResource = ReferencedResource;
38
+ /**
39
+ * The types of parsers to parse the message body
40
+ */
41
+ var ParserType;
42
+ (function (ParserType) {
43
+ ParserType["json"] = "json";
44
+ ParserType["text"] = "text";
45
+ ParserType["raw"] = "raw";
46
+ })(ParserType || (exports.ParserType = ParserType = {}));
47
+ //# sourceMappingURL=server-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-types.js","sourceRoot":"","sources":["../../../src/server/model/server-types.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;AAwBb;;GAEG;AACH,IAAY,UAQX;AARD,WAAY,UAAU;IAClB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;IACjB,2BAAa,CAAA;IACb,iCAAmB,CAAA;IACnB,6BAAe,CAAA;AACnB,CAAC,EARW,UAAU,0BAAV,UAAU,QAQrB;AAED;;GAEG;AACH,MAAa,cAAc;CAsB1B;AAtBD,wCAsBC;AAED;;GAEG;AACH,MAAsB,kBAAkB;IAMpC;;;;OAIG;IACH,YACW,QAAgB,EAChB,UAAkB;QADlB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,eAAU,GAAV,UAAU,CAAQ;IAC1B,CAAC;CACP;AAfD,gDAeC;AAwCD;;GAEG;AACH,IAAY,UAIX;AAJD,WAAY,UAAU;IAClB,2BAAa,CAAA;IACb,2BAAa,CAAA;IACb,yBAAW,CAAA;AACf,CAAC,EAJW,UAAU,0BAAV,UAAU,QAIrB"}
@@ -0,0 +1,13 @@
1
+ import { ServiceProperty } from './model/metadata';
2
+ import { ServiceContext } from './model/server-types';
3
+ export declare class ParameterProcessor {
4
+ static get(): ParameterProcessor;
5
+ private static instance;
6
+ private static defaultParamConverter;
7
+ private parameterMapper;
8
+ private debugger;
9
+ private constructor();
10
+ processParameter(context: ServiceContext, property: ServiceProperty): any;
11
+ private initializeParameterMappers;
12
+ private convertType;
13
+ }
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParameterProcessor = void 0;
4
+ const debug = require("debug");
5
+ const typescript_rest_1 = require("../typescript-rest");
6
+ const metadata_1 = require("./model/metadata");
7
+ const server_container_1 = require("./server-container");
8
+ class ParameterProcessor {
9
+ static get() {
10
+ return ParameterProcessor.instance;
11
+ }
12
+ constructor() {
13
+ this.debugger = {
14
+ build: debug('typescript-rest:parameter-processor:build'),
15
+ runtime: debug('typescript-rest:parameter-processor:runtime')
16
+ };
17
+ this.parameterMapper = this.initializeParameterMappers();
18
+ }
19
+ processParameter(context, property) {
20
+ const processor = this.parameterMapper.get(property.type);
21
+ if (!processor) {
22
+ throw new typescript_rest_1.Errors.BadRequestError('Invalid parameter type');
23
+ }
24
+ return processor(context, property);
25
+ }
26
+ initializeParameterMappers() {
27
+ this.debugger.build('Initializing parameters processors');
28
+ const parameterMapper = new Map();
29
+ parameterMapper.set(metadata_1.ParamType.path, (context, property) => this.convertType(context.request.params[property.name], property.propertyType));
30
+ parameterMapper.set(metadata_1.ParamType.query, (context, property) => this.convertType(context.request.query[property.name], property.propertyType));
31
+ parameterMapper.set(metadata_1.ParamType.header, (context, property) => this.convertType(context.request.header(property.name), property.propertyType));
32
+ parameterMapper.set(metadata_1.ParamType.cookie, (context, property) => this.convertType(context.request.cookies[property.name], property.propertyType));
33
+ parameterMapper.set(metadata_1.ParamType.body, (context, property) => this.convertType(context.request.body, property.propertyType));
34
+ parameterMapper.set(metadata_1.ParamType.file, (context, property) => {
35
+ this.debugger.runtime('Processing file parameter');
36
+ const files = context.request.files
37
+ ? // @ts-expect-error will be an array
38
+ context.request.files[property.name]
39
+ : null;
40
+ if (files && files.length > 0) {
41
+ return files[0];
42
+ }
43
+ return null;
44
+ });
45
+ parameterMapper.set(metadata_1.ParamType.files, (context, property) => {
46
+ var _a;
47
+ this.debugger.runtime('Processing files parameter');
48
+ // @ts-expect-error will be an array
49
+ return (_a = context.request.files) === null || _a === void 0 ? void 0 : _a[property.name];
50
+ });
51
+ parameterMapper.set(metadata_1.ParamType.form, (context, property) => this.convertType(context.request.body[property.name], property.propertyType));
52
+ parameterMapper.set(metadata_1.ParamType.param, (context, property) => {
53
+ const paramValue = context.request.body[property.name] || context.request.query[property.name];
54
+ return this.convertType(paramValue, property.propertyType);
55
+ });
56
+ parameterMapper.set(metadata_1.ParamType.context, (context) => context);
57
+ parameterMapper.set(metadata_1.ParamType.context_request, (context) => context.request);
58
+ parameterMapper.set(metadata_1.ParamType.context_response, (context) => context.response);
59
+ parameterMapper.set(metadata_1.ParamType.context_next, (context) => context.next);
60
+ parameterMapper.set(metadata_1.ParamType.context_accept, (context) => context.accept);
61
+ parameterMapper.set(metadata_1.ParamType.context_accept_language, (context) => context.language);
62
+ return parameterMapper;
63
+ }
64
+ convertType(paramValue, paramType) {
65
+ const serializedType = paramType['name'];
66
+ this.debugger.runtime('Processing parameter. received type: %s, received value:', serializedType, paramValue);
67
+ switch (serializedType) {
68
+ case 'Number':
69
+ return paramValue === undefined ? paramValue : parseFloat(paramValue);
70
+ case 'Boolean':
71
+ return paramValue === undefined ? paramValue : paramValue === 'true' || paramValue === true;
72
+ default:
73
+ let converter = server_container_1.ServerContainer.get().paramConverters.get(paramType);
74
+ if (!converter) {
75
+ converter = ParameterProcessor.defaultParamConverter;
76
+ }
77
+ return converter(paramValue);
78
+ }
79
+ }
80
+ }
81
+ exports.ParameterProcessor = ParameterProcessor;
82
+ ParameterProcessor.instance = new ParameterProcessor();
83
+ ParameterProcessor.defaultParamConverter = (p) => p;
84
+ //# sourceMappingURL=parameter-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parameter-processor.js","sourceRoot":"","sources":["../../src/server/parameter-processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import * as express from 'express';
2
+ import { ServiceClass, ServiceMethod } from './model/metadata';
3
+ import { FileLimits, HttpMethod, ParameterConverter, ServiceAuthenticator, ServiceFactory } from './model/server-types';
4
+ export declare class DefaultServiceFactory implements ServiceFactory {
5
+ create(serviceClass: any): any;
6
+ getTargetClass(serviceClass: Function): FunctionConstructor;
7
+ }
8
+ export declare class ServerContainer {
9
+ static get(): ServerContainer;
10
+ private static instance;
11
+ cookiesSecret: string;
12
+ cookiesDecoder: (val: string) => string;
13
+ fileDest: string;
14
+ fileFilter: (req: Express.Request, file: Express.Multer.File, callback: (error: Error, acceptFile: boolean) => void) => void;
15
+ fileLimits: FileLimits;
16
+ ignoreNextMiddlewares: boolean;
17
+ authenticator: Map<string, ServiceAuthenticator>;
18
+ serviceFactory: ServiceFactory;
19
+ paramConverters: Map<Function, ParameterConverter>;
20
+ router: express.Router;
21
+ private debugger;
22
+ private upload;
23
+ private serverClasses;
24
+ private paths;
25
+ private pathsResolved;
26
+ private constructor();
27
+ registerServiceClass(target: Function): ServiceClass;
28
+ registerServiceMethod(target: Function, methodName: string): ServiceMethod;
29
+ getPaths(): Set<string>;
30
+ getHttpMethods(path: string): Set<HttpMethod>;
31
+ buildServices(types?: Array<Function>): void;
32
+ private inheritParentClass;
33
+ private buildService;
34
+ private resolveAllPaths;
35
+ private getServiceClass;
36
+ private resolveProperties;
37
+ private resolveLanguages;
38
+ private resolveAccepts;
39
+ private resolvePath;
40
+ private validateTargetType;
41
+ private handleNotAllowedMethods;
42
+ private getUploader;
43
+ private buildServiceMiddleware;
44
+ private buildSecurityMiddlewares;
45
+ private getAuthenticator;
46
+ private buildAuthMiddleware;
47
+ private buildParserMiddlewares;
48
+ private buildBodyParserMiddleware;
49
+ private buildFilesParserMiddleware;
50
+ private buildFormParserMiddleware;
51
+ private buildJsonBodyParserMiddleware;
52
+ private buildTextBodyParserMiddleware;
53
+ private buildRawBodyParserMiddleware;
54
+ private buildCookieParserMiddleware;
55
+ }
@@ -0,0 +1,432 @@
1
+ /* eslint-disable prefer-spread */
2
+ 'use strict';
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ServerContainer = exports.DefaultServiceFactory = void 0;
14
+ const bodyParser = require("body-parser");
15
+ const cookieParser = require("cookie-parser");
16
+ const debug = require("debug");
17
+ const _ = require("lodash");
18
+ const multer = require("multer");
19
+ const Errors = require("./model/errors");
20
+ const metadata_1 = require("./model/metadata");
21
+ const server_types_1 = require("./model/server-types");
22
+ const service_invoker_1 = require("./service-invoker");
23
+ class DefaultServiceFactory {
24
+ create(serviceClass) {
25
+ return new serviceClass();
26
+ }
27
+ getTargetClass(serviceClass) {
28
+ return serviceClass;
29
+ }
30
+ }
31
+ exports.DefaultServiceFactory = DefaultServiceFactory;
32
+ class ServerContainer {
33
+ static get() {
34
+ return ServerContainer.instance;
35
+ }
36
+ constructor() {
37
+ this.ignoreNextMiddlewares = false;
38
+ this.authenticator = new Map();
39
+ this.serviceFactory = new DefaultServiceFactory();
40
+ this.paramConverters = new Map();
41
+ this.debugger = {
42
+ build: debug('typescript-rest:server-container:build'),
43
+ runtime: debug('typescript-rest:server-container:runtime')
44
+ };
45
+ this.serverClasses = new Map();
46
+ this.paths = new Map();
47
+ this.pathsResolved = false;
48
+ // noop, make the constructor private
49
+ }
50
+ registerServiceClass(target) {
51
+ this.pathsResolved = false;
52
+ target = this.serviceFactory.getTargetClass(target);
53
+ if (!this.serverClasses.has(target)) {
54
+ this.debugger.build('Registering a new service class, %o', target);
55
+ this.serverClasses.set(target, new metadata_1.ServiceClass(target));
56
+ this.inheritParentClass(target);
57
+ }
58
+ const serviceClass = this.serverClasses.get(target);
59
+ return serviceClass;
60
+ }
61
+ registerServiceMethod(target, methodName) {
62
+ if (methodName) {
63
+ this.pathsResolved = false;
64
+ const classData = this.registerServiceClass(target);
65
+ if (!classData.methods.has(methodName)) {
66
+ this.debugger.build('Registering the rest method <%s> for the service class, %o', methodName, target);
67
+ classData.methods.set(methodName, new metadata_1.ServiceMethod());
68
+ }
69
+ const serviceMethod = classData.methods.get(methodName);
70
+ return serviceMethod;
71
+ }
72
+ return null;
73
+ }
74
+ getPaths() {
75
+ this.resolveAllPaths();
76
+ const result = new Set();
77
+ this.paths.forEach((value, key) => {
78
+ result.add(key);
79
+ });
80
+ return result;
81
+ }
82
+ getHttpMethods(path) {
83
+ this.resolveAllPaths();
84
+ const methods = this.paths.get(path);
85
+ return methods || new Set();
86
+ }
87
+ buildServices(types) {
88
+ if (types) {
89
+ types = types.map((type) => this.serviceFactory.getTargetClass(type));
90
+ }
91
+ this.debugger.build('Creating service endpoints for types: %o', types);
92
+ if (this.authenticator) {
93
+ this.authenticator.forEach((auth, name) => {
94
+ this.debugger.build('Initializing authenticator: %s', name);
95
+ auth.initialize(this.router);
96
+ });
97
+ }
98
+ this.serverClasses.forEach((classData) => {
99
+ if (!classData.isAbstract) {
100
+ classData.methods.forEach((method) => {
101
+ if (this.validateTargetType(classData.targetClass, types)) {
102
+ this.buildService(classData, method);
103
+ }
104
+ });
105
+ }
106
+ });
107
+ this.pathsResolved = true;
108
+ this.handleNotAllowedMethods();
109
+ }
110
+ inheritParentClass(target) {
111
+ const classData = this.serverClasses.get(target);
112
+ const parent = Object.getPrototypeOf(classData.targetClass.prototype).constructor;
113
+ const parentClassData = this.getServiceClass(parent);
114
+ if (parentClassData) {
115
+ if (parentClassData.methods) {
116
+ parentClassData.methods.forEach((value, key) => {
117
+ classData.methods.set(key, _.cloneDeep(value));
118
+ });
119
+ }
120
+ if (parentClassData.properties) {
121
+ parentClassData.properties.forEach((value, key) => {
122
+ classData.properties.set(key, _.cloneDeep(value));
123
+ });
124
+ }
125
+ if (parentClassData.languages) {
126
+ classData.languages = _.union(classData.languages, parentClassData.languages);
127
+ }
128
+ if (parentClassData.accepts) {
129
+ classData.accepts = _.union(classData.accepts, parentClassData.accepts);
130
+ }
131
+ }
132
+ this.debugger.build('Service class registered with the given metadata: %o', classData);
133
+ }
134
+ buildService(serviceClass, serviceMethod) {
135
+ this.debugger.build('Creating service endpoint for method: %o', serviceMethod);
136
+ if (!serviceMethod.resolvedPath) {
137
+ this.resolveProperties(serviceClass, serviceMethod);
138
+ }
139
+ let args = [serviceMethod.resolvedPath];
140
+ args = args.concat(this.buildSecurityMiddlewares(serviceClass, serviceMethod));
141
+ args = args.concat(this.buildParserMiddlewares(serviceClass, serviceMethod));
142
+ args.push(this.buildServiceMiddleware(serviceMethod, serviceClass));
143
+ switch (serviceMethod.httpMethod) {
144
+ case server_types_1.HttpMethod.GET:
145
+ this.router.get.apply(this.router, args);
146
+ break;
147
+ case server_types_1.HttpMethod.POST:
148
+ this.router.post.apply(this.router, args);
149
+ break;
150
+ case server_types_1.HttpMethod.PUT:
151
+ this.router.put.apply(this.router, args);
152
+ break;
153
+ case server_types_1.HttpMethod.DELETE:
154
+ this.router.delete.apply(this.router, args);
155
+ break;
156
+ case server_types_1.HttpMethod.HEAD:
157
+ this.router.head.apply(this.router, args);
158
+ break;
159
+ case server_types_1.HttpMethod.OPTIONS:
160
+ this.router.options.apply(this.router, args);
161
+ break;
162
+ case server_types_1.HttpMethod.PATCH:
163
+ this.router.patch.apply(this.router, args);
164
+ break;
165
+ default:
166
+ throw Error(`Invalid http method for service [${serviceMethod.resolvedPath}]`);
167
+ }
168
+ }
169
+ resolveAllPaths() {
170
+ if (!this.pathsResolved) {
171
+ this.debugger.build('Building the server list of paths');
172
+ this.paths.clear();
173
+ this.serverClasses.forEach((classData) => {
174
+ classData.methods.forEach((method) => {
175
+ if (!method.resolvedPath) {
176
+ this.resolveProperties(classData, method);
177
+ }
178
+ });
179
+ });
180
+ this.pathsResolved = true;
181
+ }
182
+ }
183
+ getServiceClass(target) {
184
+ target = this.serviceFactory.getTargetClass(target);
185
+ return this.serverClasses.get(target) || null;
186
+ }
187
+ resolveProperties(serviceClass, serviceMethod) {
188
+ this.resolveLanguages(serviceClass, serviceMethod);
189
+ this.resolveAccepts(serviceClass, serviceMethod);
190
+ this.resolvePath(serviceClass, serviceMethod);
191
+ }
192
+ resolveLanguages(serviceClass, serviceMethod) {
193
+ this.debugger.build('Resolving the list of acceptable languages for method %s', serviceMethod.name);
194
+ const resolvedLanguages = _.union(serviceClass.languages, serviceMethod.languages);
195
+ if (resolvedLanguages.length > 0) {
196
+ serviceMethod.resolvedLanguages = resolvedLanguages;
197
+ }
198
+ }
199
+ resolveAccepts(serviceClass, serviceMethod) {
200
+ this.debugger.build('Resolving the list of acceptable types for method %s', serviceMethod.name);
201
+ const resolvedAccepts = _.union(serviceClass.accepts, serviceMethod.accepts);
202
+ if (resolvedAccepts.length > 0) {
203
+ serviceMethod.resolvedAccepts = resolvedAccepts;
204
+ }
205
+ }
206
+ resolvePath(serviceClass, serviceMethod) {
207
+ this.debugger.build('Resolving the path for method %s', serviceMethod.name);
208
+ const classPath = serviceClass.path ? serviceClass.path.trim() : '';
209
+ let resolvedPath = _.startsWith(classPath, '/') ? classPath : '/' + classPath;
210
+ if (_.endsWith(resolvedPath, '/')) {
211
+ resolvedPath = resolvedPath.slice(0, resolvedPath.length - 1);
212
+ }
213
+ if (serviceMethod.path) {
214
+ const methodPath = serviceMethod.path.trim();
215
+ resolvedPath = resolvedPath + (_.startsWith(methodPath, '/') ? methodPath : '/' + methodPath);
216
+ }
217
+ let declaredHttpMethods = this.paths.get(resolvedPath);
218
+ if (!declaredHttpMethods) {
219
+ declaredHttpMethods = new Set();
220
+ this.paths.set(resolvedPath, declaredHttpMethods);
221
+ }
222
+ if (declaredHttpMethods.has(serviceMethod.httpMethod)) {
223
+ throw Error(`Duplicated declaration for path [${resolvedPath}], method [${serviceMethod.httpMethod}].`);
224
+ }
225
+ declaredHttpMethods.add(serviceMethod.httpMethod);
226
+ serviceMethod.resolvedPath = resolvedPath;
227
+ }
228
+ validateTargetType(targetClass, types) {
229
+ if (types && types.length > 0) {
230
+ return types.indexOf(targetClass) > -1;
231
+ }
232
+ return true;
233
+ }
234
+ handleNotAllowedMethods() {
235
+ this.debugger.build('Creating middleware to handle not allowed methods');
236
+ const paths = this.getPaths();
237
+ paths.forEach((path) => {
238
+ const supported = this.getHttpMethods(path);
239
+ const allowedMethods = new Array();
240
+ supported.forEach((method) => {
241
+ allowedMethods.push(server_types_1.HttpMethod[method]);
242
+ });
243
+ const allowed = allowedMethods.join(', ');
244
+ this.debugger.build('Registering middleware to validate allowed HTTP methods for path %s.', path);
245
+ this.debugger.build('Allowed HTTP methods [%s].', allowed);
246
+ this.router.all(path, (req, res, next) => {
247
+ if (res.headersSent || allowedMethods.indexOf(req.method) > -1) {
248
+ next();
249
+ }
250
+ else {
251
+ res.set('Allow', allowed);
252
+ throw new Errors.MethodNotAllowedError();
253
+ }
254
+ });
255
+ });
256
+ }
257
+ getUploader() {
258
+ if (!this.upload) {
259
+ const options = {};
260
+ if (this.fileDest) {
261
+ options.dest = this.fileDest;
262
+ }
263
+ if (this.fileFilter) {
264
+ options.fileFilter = this.fileFilter;
265
+ }
266
+ if (this.fileLimits) {
267
+ options.limits = this.fileLimits;
268
+ }
269
+ if (options.dest) {
270
+ this.debugger.build('Creating a file Uploader with options: %o.', options);
271
+ this.upload = multer(options);
272
+ }
273
+ else {
274
+ this.debugger.build('Creating a file Uploader with the default options.');
275
+ this.upload = multer();
276
+ }
277
+ }
278
+ return this.upload;
279
+ }
280
+ buildServiceMiddleware(serviceMethod, serviceClass) {
281
+ const serviceInvoker = new service_invoker_1.ServiceInvoker(serviceClass, serviceMethod);
282
+ this.debugger.build('Creating the service middleware for method <%s>.', serviceMethod.name);
283
+ return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
284
+ const context = new server_types_1.ServiceContext();
285
+ context.request = req;
286
+ context.response = res;
287
+ context.next = next;
288
+ yield serviceInvoker.callService(context);
289
+ });
290
+ }
291
+ buildSecurityMiddlewares(serviceClass, serviceMethod) {
292
+ const result = new Array();
293
+ const authenticatorMap = serviceMethod.authenticator || serviceClass.authenticator;
294
+ if (this.authenticator && authenticatorMap) {
295
+ const authenticatorNames = Object.keys(authenticatorMap);
296
+ for (const authenticatorName of authenticatorNames) {
297
+ let roles = authenticatorMap[authenticatorName];
298
+ this.debugger.build('Registering an authenticator middleware <%s> for method <%s>.', authenticatorName, serviceMethod.name);
299
+ const authenticator = this.getAuthenticator(authenticatorName);
300
+ result.push(authenticator.getMiddleware());
301
+ roles = roles.filter((role) => role !== '*');
302
+ if (roles.length) {
303
+ this.debugger.build('Registering a role validator middleware <%s> for method <%s>.', authenticatorName, serviceMethod.name);
304
+ this.debugger.build('Roles: <%j>.', roles);
305
+ result.push(this.buildAuthMiddleware(authenticator, roles));
306
+ }
307
+ }
308
+ }
309
+ return result;
310
+ }
311
+ getAuthenticator(authenticatorName) {
312
+ if (!this.authenticator.has(authenticatorName)) {
313
+ throw new Error(`Invalid authenticator name ${authenticatorName}`);
314
+ }
315
+ return this.authenticator.get(authenticatorName);
316
+ }
317
+ buildAuthMiddleware(authenticator, roles) {
318
+ return (req, res, next) => {
319
+ const requestRoles = authenticator.getRoles(req, res);
320
+ if (this.debugger.runtime.enabled) {
321
+ this.debugger.runtime('Validating authentication roles: <%j>.', requestRoles);
322
+ }
323
+ if (requestRoles.some((role) => roles.indexOf(role) >= 0)) {
324
+ next();
325
+ }
326
+ else {
327
+ throw new Errors.ForbiddenError();
328
+ }
329
+ };
330
+ }
331
+ buildParserMiddlewares(serviceClass, serviceMethod) {
332
+ const result = new Array();
333
+ const bodyParserOptions = serviceMethod.bodyParserOptions || serviceClass.bodyParserOptions;
334
+ if (serviceMethod.mustParseCookies) {
335
+ this.debugger.build('Registering cookie parser middleware for method <%s>.', serviceMethod.name);
336
+ result.push(this.buildCookieParserMiddleware());
337
+ }
338
+ if (serviceMethod.mustParseBody) {
339
+ const bodyParserType = serviceMethod.bodyParserType || serviceClass.bodyParserType || server_types_1.ParserType.json;
340
+ this.debugger.build('Registering body %s parser middleware for method <%s>' + ' with options: %j.', server_types_1.ParserType[bodyParserType], serviceMethod.name, bodyParserOptions);
341
+ result.push(this.buildBodyParserMiddleware(serviceMethod, bodyParserOptions, bodyParserType));
342
+ }
343
+ if (serviceMethod.mustParseForms || serviceMethod.acceptMultiTypedParam) {
344
+ this.debugger.build('Registering body form parser middleware for method <%s>' + ' with options: %j.', serviceMethod.name, bodyParserOptions);
345
+ result.push(this.buildFormParserMiddleware(bodyParserOptions));
346
+ }
347
+ if (serviceMethod.files.length > 0) {
348
+ this.debugger.build('Registering file parser middleware for method <%s>.', serviceMethod.name);
349
+ result.push(this.buildFilesParserMiddleware(serviceMethod));
350
+ }
351
+ return result;
352
+ }
353
+ buildBodyParserMiddleware(serviceMethod, bodyParserOptions, bodyParserType) {
354
+ switch (bodyParserType) {
355
+ case server_types_1.ParserType.text:
356
+ return this.buildTextBodyParserMiddleware(bodyParserOptions);
357
+ case server_types_1.ParserType.raw:
358
+ return this.buildRawBodyParserMiddleware(bodyParserOptions);
359
+ default:
360
+ return this.buildJsonBodyParserMiddleware(bodyParserOptions);
361
+ }
362
+ }
363
+ buildFilesParserMiddleware(serviceMethod) {
364
+ const options = new Array();
365
+ serviceMethod.files.forEach((fileData) => {
366
+ if (fileData.singleFile) {
367
+ options.push({ name: fileData.name, maxCount: 1 });
368
+ }
369
+ else {
370
+ options.push({ name: fileData.name });
371
+ }
372
+ });
373
+ this.debugger.build('Creating file parser with options %j.', options);
374
+ return this.getUploader().fields(options);
375
+ }
376
+ buildFormParserMiddleware(bodyParserOptions) {
377
+ if (!bodyParserOptions) {
378
+ bodyParserOptions = { extended: true };
379
+ }
380
+ this.debugger.build('Creating form body parser with options %j.', bodyParserOptions);
381
+ const middleware = bodyParser.urlencoded(bodyParserOptions);
382
+ return middleware;
383
+ }
384
+ buildJsonBodyParserMiddleware(bodyParserOptions) {
385
+ let middleware;
386
+ this.debugger.build('Creating json body parser with options %j.', bodyParserOptions || {});
387
+ if (bodyParserOptions) {
388
+ middleware = bodyParser.json(bodyParserOptions);
389
+ }
390
+ else {
391
+ middleware = bodyParser.json();
392
+ }
393
+ return middleware;
394
+ }
395
+ buildTextBodyParserMiddleware(bodyParserOptions) {
396
+ let middleware;
397
+ this.debugger.build('Creating text body parser with options %j.', bodyParserOptions || {});
398
+ if (bodyParserOptions) {
399
+ middleware = bodyParser.text(bodyParserOptions);
400
+ }
401
+ else {
402
+ middleware = bodyParser.text();
403
+ }
404
+ return middleware;
405
+ }
406
+ buildRawBodyParserMiddleware(bodyParserOptions) {
407
+ let middleware;
408
+ this.debugger.build('Creating raw body parser with options %j.', bodyParserOptions || {});
409
+ if (bodyParserOptions) {
410
+ middleware = bodyParser.raw(bodyParserOptions);
411
+ }
412
+ else {
413
+ middleware = bodyParser.raw();
414
+ }
415
+ return middleware;
416
+ }
417
+ buildCookieParserMiddleware() {
418
+ const args = [];
419
+ if (this.cookiesSecret) {
420
+ args.push(this.cookiesSecret);
421
+ }
422
+ if (this.cookiesDecoder) {
423
+ args.push({ decode: this.cookiesDecoder });
424
+ }
425
+ this.debugger.build('Creating cookie parser with options %j.', args);
426
+ const middleware = cookieParser.apply(this, args);
427
+ return middleware;
428
+ }
429
+ }
430
+ exports.ServerContainer = ServerContainer;
431
+ ServerContainer.instance = new ServerContainer();
432
+ //# sourceMappingURL=server-container.js.map