@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,390 @@
1
+ 'use strict';
2
+
3
+ import * as _ from 'lodash';
4
+ import 'reflect-metadata';
5
+ import { ServiceClass, ServiceMethod } from '../server/model/metadata';
6
+ import { ParserType, ServiceProcessor } from '../server/model/server-types';
7
+ import { ServerContainer } from '../server/server-container';
8
+
9
+ /**
10
+ * A decorator to tell the [[Server]] that a class or a method
11
+ * should be bound to a given path.
12
+ *
13
+ * For example:
14
+ *
15
+ * ```
16
+ * @ Path('people')
17
+ * class PeopleService {
18
+ * @ PUT
19
+ * @ Path(':id')
20
+ * savePerson(person:Person) {
21
+ * // ...
22
+ * }
23
+ *
24
+ * @ GET
25
+ * @ Path(':id')
26
+ * getPerson():Person {
27
+ * // ...
28
+ * }
29
+ * }
30
+ * ```
31
+ *
32
+ * Will create services that listen for requests like:
33
+ *
34
+ * ```
35
+ * PUT http://mydomain/people/123 or
36
+ * GET http://mydomain/people/123
37
+ * ```
38
+ */
39
+ export function Path(path: string) {
40
+ return new ServiceDecorator('Path').withProperty('path', path).createDecorator();
41
+ }
42
+
43
+ /**
44
+ * A decorator to tell the [[Server]] that a class or a method
45
+ * should include a determined role
46
+ * or all authorized users (token) using passport
47
+ *
48
+ * For example:
49
+ *
50
+ * ```
51
+ * @ Path('people')
52
+ * @ Security()
53
+ * class PeopleService {
54
+ * @ PUT
55
+ * @ Path(':id', true)
56
+ * @ Security(['ROLE_ADMIN'])
57
+ * savePerson(person:Person) {
58
+ * // ...
59
+ * }
60
+ *
61
+ * @ GET
62
+ * @ Path(':id', true)
63
+ * getPerson():Person {
64
+ * // ...
65
+ * }
66
+ * }
67
+ * ```
68
+ *
69
+ * Will create services that listen for requests like:
70
+ *
71
+ * ```
72
+ * PUT http://mydomain/people/123 (Only for ADMIN roles) or
73
+ * GET http://mydomain/people/123 (For all authorized users)
74
+ * ```
75
+ */
76
+ export function Security(roles?: string | Array<string>, name?: string) {
77
+ roles = _.castArray(roles || '*');
78
+ return new SecurityServiceDecorator('Security')
79
+ .withObjectProperty('authenticator', name || 'default', roles)
80
+ .createDecorator();
81
+ }
82
+
83
+ /**
84
+ * A decorator to tell the [[Server]] that a class or a method
85
+ * should include a pre-processor in its request pipelines.
86
+ *
87
+ * For example:
88
+ * ```
89
+ * function validator(req: express.Request): express.Request {
90
+ * if (!req.body.userId) {
91
+ * throw new Errors.BadRequestError("userId not present");
92
+ * }
93
+ * }
94
+ * ```
95
+ * And:
96
+ *
97
+ * ```
98
+ * @ Path('people')
99
+ * class PeopleService {
100
+ * @ PUT
101
+ * @ Path(':id')
102
+ * @ PreProcessor(validator)
103
+ * savePerson(person:Person) {
104
+ * // ...
105
+ * }
106
+ * }
107
+ * ```
108
+ */
109
+ export function PreProcessor(preprocessor: ServiceProcessor) {
110
+ return new ProcessorServiceDecorator('PreProcessor')
111
+ .withArrayProperty('preProcessors', preprocessor, true)
112
+ .createDecorator();
113
+ }
114
+
115
+ /**
116
+ * A decorator to tell the [[Server]] that a class or a method
117
+ * should include a post-processor in its request pipelines.
118
+ *
119
+ * For example:
120
+ * ```
121
+ * function processor(req: express.Request): express.Request {
122
+ * if (!req.body.userId) {
123
+ * throw new Errors.BadRequestError("userId not present");
124
+ * }
125
+ * }
126
+ * ```
127
+ * And:
128
+ *
129
+ * ```
130
+ * @ Path('people')
131
+ * class PeopleService {
132
+ * @ PUT
133
+ * @ Path(':id')
134
+ * @ PostProcessor(validator)
135
+ * savePerson(person:Person) {
136
+ * // ...
137
+ * }
138
+ * }
139
+ * ```
140
+ */
141
+ export function PostProcessor(postprocessor: ServiceProcessor) {
142
+ return new ProcessorServiceDecorator('PostProcessor')
143
+ .withArrayProperty('postProcessors', postprocessor, true)
144
+ .createDecorator();
145
+ }
146
+
147
+ /**
148
+ * A decorator to tell the [[Server]] that a class or a method
149
+ * should only accept requests from clients that accepts one of
150
+ * the supported languages.
151
+ *
152
+ * For example:
153
+ *
154
+ * ```
155
+ * @ Path('accept')
156
+ * @ AcceptLanguage('en', 'pt-BR')
157
+ * class TestAcceptService {
158
+ * // ...
159
+ * }
160
+ * ```
161
+ *
162
+ * Will reject requests that only accepts languages that are not
163
+ * English or Brazilian portuguese
164
+ *
165
+ * If the language requested is not supported, a status code 406 returned
166
+ */
167
+ export function AcceptLanguage(...languages: Array<string>) {
168
+ languages = _.compact(languages);
169
+ return new AcceptServiceDecorator('AcceptLanguage')
170
+ .withArrayProperty('languages', languages, true)
171
+ .createDecorator();
172
+ }
173
+
174
+ /**
175
+ * A decorator to tell the [[Server]] that a class or a method
176
+ * should only accept requests from clients that accepts one of
177
+ * the supported mime types.
178
+ *
179
+ * For example:
180
+ *
181
+ * ```
182
+ * @ Path('accept')
183
+ * @ Accept('application/json')
184
+ * class TestAcceptService {
185
+ * // ...
186
+ * }
187
+ * ```
188
+ *
189
+ * Will reject requests that only accepts mime types that are not
190
+ * 'application/json'
191
+ *
192
+ * If the mime type requested is not supported, a status code 406 returned
193
+ */
194
+ export function Accept(...accepts: Array<string>) {
195
+ accepts = _.compact(accepts);
196
+ return new AcceptServiceDecorator('Accept').withArrayProperty('accepts', accepts, true).createDecorator();
197
+ }
198
+
199
+ /**
200
+ * A decorator to inform options to pe passed to bodyParser.
201
+ * You can inform any property accepted by
202
+ * [[bodyParser]](https://www.npmjs.com/package/body-parser)
203
+ */
204
+ export function BodyOptions(options: any) {
205
+ return new ServiceDecorator('BodyOptions').withProperty('bodyParserOptions', options).createDecorator();
206
+ }
207
+
208
+ /**
209
+ * A decorator to inform the type of parser to be used to parse the body.
210
+ * The default type is json.
211
+ */
212
+ export function BodyType(type: ParserType) {
213
+ return new ServiceDecorator('BodyType').withProperty('bodyParserType', type).createDecorator();
214
+ }
215
+
216
+ /**
217
+ * A decorator to inform that server should ignore other middlewares.
218
+ * It makes server does not call next function after service invocation.
219
+ */
220
+ export function IgnoreNextMiddlewares(...args: Array<any>) {
221
+ return new ServiceDecorator('IgnoreNextMiddlewares')
222
+ .withProperty('ignoreNextMiddlewares', true)
223
+ .decorateTypeOrMethod(args);
224
+ }
225
+
226
+ /**
227
+ * Mark the annotated service class as an abstract service. Abstract services has none of its
228
+ * methods exposed as rest enpoints, even if the class is in the services list to be exposed.
229
+ *
230
+ * For example:
231
+ *
232
+ * ```
233
+ * @ Abstract
234
+ * abstract class PeopleService {
235
+ * @ GET
236
+ * getPeople(@ Param('name') name: string) {
237
+ * // ...
238
+ * }
239
+ * }
240
+ * ```
241
+ *
242
+ * No endpoint will be registered for PeopleService. It is useful if you only plain that subclasses of
243
+ * PeopleService exposes the getPeople method.
244
+ */
245
+ export function Abstract(...args: Array<any>) {
246
+ args = _.without(args, undefined);
247
+ if (args.length === 1) {
248
+ const classData: ServiceClass = ServerContainer.get().registerServiceClass(args[0]);
249
+ classData.isAbstract = true;
250
+ } else {
251
+ throw new Error('Invalid @Abstract Decorator declaration.');
252
+ }
253
+ }
254
+
255
+ interface DecoratorProperty {
256
+ property: string;
257
+ value: any;
258
+ required: boolean;
259
+ process: (target: any) => void;
260
+ checkRequired: () => boolean;
261
+ }
262
+
263
+ class ServiceDecorator {
264
+ protected decorator: string;
265
+ protected properties: Array<DecoratorProperty> = [];
266
+
267
+ constructor(decorator: string) {
268
+ this.decorator = decorator;
269
+ }
270
+
271
+ public withProperty(property: string, value: any, required: boolean = false) {
272
+ this.properties.push({
273
+ checkRequired: () => required && !value,
274
+ process: (target: any) => {
275
+ target[property] = value;
276
+ },
277
+ property: property,
278
+ required: required,
279
+ value: value
280
+ });
281
+ return this;
282
+ }
283
+
284
+ public createDecorator() {
285
+ return (...args: Array<any>) => {
286
+ this.checkRequiredValue();
287
+ this.decorateTypeOrMethod(args);
288
+ };
289
+ }
290
+
291
+ public decorateTypeOrMethod(args: Array<any>) {
292
+ args = _.without(args, undefined);
293
+ if (args.length === 1) {
294
+ this.decorateType(args[0]);
295
+ } else if (args.length === 3 && typeof args[2] === 'object') {
296
+ this.decorateMethod(args[0], args[1]);
297
+ } else {
298
+ throw new Error(`Invalid @${this.decorator} Decorator declaration.`);
299
+ }
300
+ }
301
+
302
+ protected checkRequiredValue() {
303
+ this.properties.forEach((property) => {
304
+ if (property.checkRequired()) {
305
+ throw new Error(`Invalid @${this.decorator} Decorator declaration.`);
306
+ }
307
+ });
308
+ }
309
+
310
+ protected decorateType(target: Function) {
311
+ const classData: ServiceClass = ServerContainer.get().registerServiceClass(target);
312
+ if (classData) {
313
+ this.updateClassMetadata(classData);
314
+ }
315
+ }
316
+
317
+ protected decorateMethod(target: Function, propertyKey: string) {
318
+ const serviceMethod: ServiceMethod = ServerContainer.get().registerServiceMethod(
319
+ target.constructor,
320
+ propertyKey
321
+ );
322
+ if (serviceMethod) {
323
+ // does not intercept constructor
324
+ this.updateMethodMetadada(serviceMethod);
325
+ }
326
+ }
327
+
328
+ protected updateClassMetadata(classData: ServiceClass) {
329
+ this.properties.forEach((property) => {
330
+ property.process(classData);
331
+ });
332
+ }
333
+
334
+ protected updateMethodMetadada(serviceMethod: ServiceMethod) {
335
+ this.properties.forEach((property) => {
336
+ property.process(serviceMethod);
337
+ });
338
+ }
339
+ }
340
+
341
+ class SecurityServiceDecorator extends ServiceDecorator {
342
+ public withObjectProperty(property: string, subtext: string, value: any, required: boolean = false) {
343
+ this.properties.push({
344
+ checkRequired: () => required && !value,
345
+ process: (target: any) => {
346
+ if (!target[property]) {
347
+ target[property] = {};
348
+ }
349
+ target[property][subtext] = value;
350
+ },
351
+ property: property,
352
+ required: required,
353
+ value: value
354
+ });
355
+ return this;
356
+ }
357
+ }
358
+
359
+ class ProcessorServiceDecorator extends ServiceDecorator {
360
+ public withArrayProperty(property: string, value: any, required: boolean = false) {
361
+ this.properties.push({
362
+ checkRequired: () => required && !value,
363
+ process: (target: any) => {
364
+ if (!target[property]) {
365
+ target[property] = [];
366
+ }
367
+ target[property].unshift(value);
368
+ },
369
+ property: property,
370
+ required: required,
371
+ value: value
372
+ });
373
+ return this;
374
+ }
375
+ }
376
+
377
+ class AcceptServiceDecorator extends ServiceDecorator {
378
+ public withArrayProperty(property: string, value: any, required: boolean = false) {
379
+ this.properties.push({
380
+ checkRequired: () => required && (!value || !value.length),
381
+ process: (target: any) => {
382
+ target[property] = _.union(target[property], value);
383
+ },
384
+ property: property,
385
+ required: required,
386
+ value: value
387
+ });
388
+ return this;
389
+ }
390
+ }
@@ -0,0 +1,40 @@
1
+ import * as debug from 'debug';
2
+ import * as fs from 'fs-extra';
3
+ import * as path from 'path';
4
+ import { Server } from './server';
5
+
6
+ const serverDebugger = debug('typescript-rest:server:config:build');
7
+
8
+ export class ServerConfig {
9
+ public static configure() {
10
+ try {
11
+ const CONFIG_FILE = this.searchConfigFile();
12
+ if (CONFIG_FILE && fs.existsSync(CONFIG_FILE)) {
13
+ const config = fs.readJSONSync(CONFIG_FILE);
14
+ serverDebugger('rest.config file found: %j', config);
15
+ if (config.serviceFactory) {
16
+ if (config.serviceFactory.indexOf('.') === 0) {
17
+ config.serviceFactory = path.join(process.cwd(), config.serviceFactory);
18
+ }
19
+ Server.registerServiceFactory(config.serviceFactory);
20
+ }
21
+ }
22
+ } catch (e) {
23
+ // eslint-disable-next-line no-console
24
+ console.error(e);
25
+ }
26
+ }
27
+
28
+ public static searchConfigFile() {
29
+ serverDebugger('Searching for rest.config file');
30
+ let configFile = path.join(__dirname, 'rest.config');
31
+ while (!fs.existsSync(configFile)) {
32
+ const fileOnParent = path.normalize(path.join(path.dirname(configFile), '..', 'rest.config'));
33
+ if (configFile === fileOnParent) {
34
+ return null;
35
+ }
36
+ configFile = fileOnParent;
37
+ }
38
+ return configFile;
39
+ }
40
+ }
@@ -0,0 +1,178 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * The Base class for all HTTP errors
5
+ */
6
+ export abstract class HttpError extends Error {
7
+ public statusCode: number;
8
+
9
+ constructor(
10
+ name: string,
11
+ public message: string
12
+ ) {
13
+ super(message);
14
+ this.name = name;
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Represents a BAD REQUEST error. The request could not be understood by the
20
+ * server due to malformed syntax. The client SHOULD NOT repeat the request
21
+ * without modifications.
22
+ */
23
+ export class BadRequestError extends HttpError {
24
+ constructor(message?: string) {
25
+ super('BadRequestError', message || 'Bad Request');
26
+ Object.setPrototypeOf(this, BadRequestError.prototype);
27
+ this.statusCode = 400;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Represents an UNAUTHORIZED error. The request requires user authentication. The response
33
+ * MUST include a WWW-Authenticate header field containing a challenge applicable to the
34
+ * requested resource.
35
+ */
36
+ export class UnauthorizedError extends HttpError {
37
+ constructor(message?: string) {
38
+ super('UnauthorizedError', message || 'Unauthorized');
39
+ Object.setPrototypeOf(this, UnauthorizedError.prototype);
40
+ this.statusCode = 401;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Represents a FORBIDDEN error. The server understood the request, but is refusing to
46
+ * fulfill it. Authorization will not help and the request SHOULD NOT be repeated.
47
+ */
48
+ export class ForbiddenError extends HttpError {
49
+ constructor(message?: string) {
50
+ super('ForbiddenError', message || 'Forbidden');
51
+ Object.setPrototypeOf(this, ForbiddenError.prototype);
52
+ this.statusCode = 403;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Represents a NOT FOUND error. The server has not found anything matching
58
+ * the Request-URI. No indication is given of whether the condition is temporary
59
+ * or permanent. The 410 (GoneError) status code SHOULD be used if the server knows,
60
+ * through some internally configurable mechanism, that an old resource is permanently
61
+ * unavailable and has no forwarding address.
62
+ *
63
+ * This error is commonly used when
64
+ * the server does not wish to reveal exactly why the request has been refused,
65
+ * or when no other response is applicable.
66
+ */
67
+ export class NotFoundError extends HttpError {
68
+ constructor(message?: string) {
69
+ super('NotFoundError', message || 'Not Found');
70
+ Object.setPrototypeOf(this, NotFoundError.prototype);
71
+ this.statusCode = 404;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Represents a METHOD NOT ALLOWED error. The method specified in the Request-Line is not allowed for
77
+ * the resource identified by the Request-URI. The response MUST include an Allow header
78
+ * containing a list of valid methods for the requested resource.
79
+ */
80
+ export class MethodNotAllowedError extends HttpError {
81
+ constructor(message?: string) {
82
+ super('MethodNotAllowedError', message || 'Method Not Allowed');
83
+ Object.setPrototypeOf(this, MethodNotAllowedError.prototype);
84
+ this.statusCode = 405;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Represents a NOT ACCEPTABLE error. The resource identified by the request is only capable of
90
+ * generating response entities which have content characteristics not acceptable according
91
+ * to the accept headers sent in the request.
92
+ */
93
+ export class NotAcceptableError extends HttpError {
94
+ constructor(message?: string) {
95
+ super('NotAcceptableError', message || 'Not Acceptable');
96
+ Object.setPrototypeOf(this, NotAcceptableError.prototype);
97
+ this.statusCode = 406;
98
+ }
99
+ }
100
+ /**
101
+ * Represents a CONFLICT error. The request could not be completed due to a
102
+ * conflict with the current state of the resource.
103
+ */
104
+ export class ConflictError extends HttpError {
105
+ constructor(message?: string) {
106
+ super('ConflictError', message || 'Conflict');
107
+ Object.setPrototypeOf(this, ConflictError.prototype);
108
+ this.statusCode = 409;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Represents a GONE error. The requested resource is no longer available at the server
114
+ * and no forwarding address is known. This condition is expected to be considered
115
+ * permanent. Clients with link editing capabilities SHOULD delete references to
116
+ * the Request-URI after user approval. If the server does not know, or has
117
+ * no facility to determine, whether or not the condition is permanent, the
118
+ * error 404 (NotFoundError) SHOULD be used instead. This response is
119
+ * cacheable unless indicated otherwise.
120
+ */
121
+ export class GoneError extends HttpError {
122
+ constructor(message?: string) {
123
+ super('GoneError', message || 'Gone');
124
+ Object.setPrototypeOf(this, GoneError.prototype);
125
+ this.statusCode = 410;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Represents an UNSUPPORTED MEDIA TYPE error. The server is refusing to service the request
131
+ * because the entity of the request is in a format not supported by the requested resource
132
+ * for the requested method.
133
+ */
134
+ export class UnsupportedMediaTypeError extends HttpError {
135
+ constructor(message?: string) {
136
+ super('UnsupportedMediaTypeError', message || 'Unsupported Media Type');
137
+ Object.setPrototypeOf(this, UnsupportedMediaTypeError.prototype);
138
+ this.statusCode = 415;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Represents a UNPROCESSABLE ENTITY error. The server understands the content type of the request entity
144
+ * (hence a 415 Unsupported Media Type status code is inappropriate), and the syntax of the request entity is correct
145
+ * (thus a 400 Bad Request status code is inappropriate) but was unable to process the contained instructions.
146
+ */
147
+ export class UnprocessableEntityError extends HttpError {
148
+ constructor(message?: string) {
149
+ super('UnprocessableEntityError', message || 'Unprocessable Entity');
150
+ Object.setPrototypeOf(this, UnprocessableEntityError.prototype);
151
+ this.statusCode = 422;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Represents an INTERNAL SERVER error. The server encountered an unexpected condition
157
+ * which prevented it from fulfilling the request.
158
+ */
159
+ export class InternalServerError extends HttpError {
160
+ constructor(message?: string) {
161
+ super('InternalServerError', message || 'Internal Server Error');
162
+ Object.setPrototypeOf(this, InternalServerError.prototype);
163
+ this.statusCode = 500;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Represents a NOT IMPLEMENTED error. The server does not support the functionality required
169
+ * to fulfill the request. This is the appropriate response when the server does not recognize
170
+ * the request method and is not capable of supporting it for any resource.
171
+ */
172
+ export class NotImplementedError extends HttpError {
173
+ constructor(message?: string) {
174
+ super('NotImplementedError', message || 'Not Implemented');
175
+ Object.setPrototypeOf(this, NotImplementedError.prototype);
176
+ this.statusCode = 501;
177
+ }
178
+ }