@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,118 @@
1
+ 'use strict';
2
+
3
+ import { HttpMethod, ParserType, ServiceProcessor } from './server-types';
4
+
5
+ export interface ServiceProperty {
6
+ type: ParamType;
7
+ name: string;
8
+ propertyType: any;
9
+ }
10
+
11
+ /**
12
+ * Metadata for REST service classes
13
+ */
14
+ export class ServiceClass {
15
+ [key: string]: any;
16
+
17
+ public targetClass: any;
18
+ public path: string;
19
+ public authenticator: Record<string, Array<string>>;
20
+ public preProcessors: Array<ServiceProcessor>;
21
+ public postProcessors: Array<ServiceProcessor>;
22
+ public methods: Map<string, ServiceMethod>;
23
+ public bodyParserOptions: any;
24
+ public bodyParserType: ParserType;
25
+ public languages: Array<string>;
26
+ public accepts: Array<string>;
27
+ public properties: Map<string, ServiceProperty>;
28
+ public isAbstract: boolean = false;
29
+ public ignoreNextMiddlewares: boolean = false;
30
+ constructor(targetClass: any) {
31
+ this.targetClass = targetClass;
32
+ this.methods = new Map<string, ServiceMethod>();
33
+ this.properties = new Map<string, ServiceProperty>();
34
+ }
35
+
36
+ public addProperty(key: string, property: ServiceProperty) {
37
+ this.properties.set(key, property);
38
+ }
39
+
40
+ public hasProperties(): boolean {
41
+ return this.properties && this.properties.size > 0;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Metadata for REST service methods
47
+ */
48
+ export class ServiceMethod {
49
+ [key: string]: any;
50
+
51
+ public name: string;
52
+ public path: string;
53
+ public authenticator: Record<string, Array<string>>;
54
+ public resolvedPath: string;
55
+ public httpMethod: HttpMethod;
56
+ public parameters: Array<MethodParam> = new Array<MethodParam>();
57
+ public mustParseCookies: boolean = false;
58
+ public files: Array<FileParam> = new Array<FileParam>();
59
+ public mustParseBody: boolean = false;
60
+ public bodyParserOptions: any;
61
+ public bodyParserType: ParserType;
62
+ public mustParseForms: boolean = false;
63
+ public acceptMultiTypedParam: boolean = false;
64
+ public languages: Array<string>;
65
+ public accepts: Array<string>;
66
+ public resolvedLanguages: Array<string>;
67
+ public resolvedAccepts: Array<string>;
68
+ public preProcessors: Array<ServiceProcessor>;
69
+ public postProcessors: Array<ServiceProcessor>;
70
+ public ignoreNextMiddlewares: boolean = false;
71
+ }
72
+
73
+ /**
74
+ * Metadata for File parameters on REST methods
75
+ */
76
+ export class FileParam {
77
+ public name: string;
78
+ public singleFile: boolean;
79
+ constructor(name: string, singleFile: boolean) {
80
+ this.name = name;
81
+ this.singleFile = singleFile;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Metadata for REST service method parameters
87
+ */
88
+ export class MethodParam {
89
+ public name: string;
90
+ public type: Function;
91
+ public paramType: ParamType;
92
+ constructor(name: string, type: Function, paramType: ParamType) {
93
+ this.name = name;
94
+ this.type = type;
95
+ this.paramType = paramType;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Enumeration of accepted parameter types
101
+ */
102
+ export enum ParamType {
103
+ path = 'path',
104
+ query = 'query',
105
+ header = 'header',
106
+ cookie = 'cookie',
107
+ form = 'form',
108
+ body = 'body',
109
+ param = 'param',
110
+ file = 'file',
111
+ files = 'files',
112
+ context = 'context',
113
+ context_request = 'context_request',
114
+ context_response = 'context_response',
115
+ context_next = 'context_next',
116
+ context_accept = 'context_accept',
117
+ context_accept_language = 'context_accept_language'
118
+ }
@@ -0,0 +1,109 @@
1
+ 'use strict';
2
+
3
+ import { ReferencedResource } from './server-types';
4
+
5
+ /**
6
+ * Inform that a new resource was created. Server will
7
+ * add a Location header and set status to 201
8
+ */
9
+ export class NewResource<T> extends ReferencedResource<T> {
10
+ /**
11
+ * Constructor. Receives the location of the new resource created.
12
+ * @param location To be added to the Location header on response
13
+ * @param body To be added to the response body
14
+ */
15
+ constructor(location: string, body?: T) {
16
+ super(location, 201);
17
+ this.body = body;
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Inform that the request was accepted but is not completed.
23
+ * A Location header should inform the location where the user
24
+ * can monitor his request processing status.
25
+ */
26
+ export class RequestAccepted<T> extends ReferencedResource<T> {
27
+ /**
28
+ * Constructor. Receives the location where information about the
29
+ * request processing can be found.
30
+ * @param location To be added to the Location header on response
31
+ * @param body To be added to the response body
32
+ */
33
+ constructor(location: string, body?: T) {
34
+ super(location, 202);
35
+ this.body = body;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Inform that the resource has permanently
41
+ * moved to a new location, and that future references should use a
42
+ * new URI with their requests.
43
+ */
44
+ export class MovedPermanently<T> extends ReferencedResource<T> {
45
+ /**
46
+ * Constructor. Receives the location where the resource can be found.
47
+ * @param location To be added to the Location header on response
48
+ * @param body To be added to the response body
49
+ */
50
+ constructor(location: string, body?: T) {
51
+ super(location, 301);
52
+ this.body = body;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Inform that the resource has temporarily
58
+ * moved to another location, but that future references should
59
+ * still use the original URI to access the resource.
60
+ */
61
+ export class MovedTemporarily<T> extends ReferencedResource<T> {
62
+ /**
63
+ * Constructor. Receives the location where the resource can be found.
64
+ * @param location To be added to the Location header on response
65
+ * @param body To be added to the response body
66
+ */
67
+ constructor(location: string, body?: T) {
68
+ super(location, 302);
69
+ this.body = body;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Used to download a resource.
75
+ */
76
+ export class DownloadResource {
77
+ /**
78
+ * Constructor.
79
+ * @param filePath The file path to download.
80
+ * @param fileName The file name
81
+ */
82
+ constructor(
83
+ public filePath: string,
84
+ public fileName: string
85
+ ) {}
86
+ }
87
+
88
+ /**
89
+ * Used to download binary data as a file.
90
+ */
91
+ export class DownloadBinaryData {
92
+ /**
93
+ * Constructor. Receives the location of the resource.
94
+ * @param content The binary data to be downloaded as a file.
95
+ * @param mimeType The mime-type to be passed on Content-Type header.
96
+ * @param fileName The file name
97
+ */
98
+ constructor(
99
+ public content: Buffer,
100
+ public mimeType: string,
101
+ public fileName?: string
102
+ ) {}
103
+ }
104
+
105
+ /**
106
+ * If returned by a service, no response will be sent to client. Use it
107
+ * if you want to send the response by yourself.
108
+ */
109
+ export const NoResponse = {};
@@ -0,0 +1,130 @@
1
+ 'use strict';
2
+
3
+ import * as express from 'express';
4
+
5
+ /**
6
+ * Limits for file uploads
7
+ */
8
+ export interface FileLimits {
9
+ /** Max field name size (Default: 100 bytes) */
10
+ fieldNameSize?: number;
11
+ /** Max field value size (Default: 1MB) */
12
+ fieldSize?: number;
13
+ /** Max number of non- file fields (Default: Infinity) */
14
+ fields?: number;
15
+ /** For multipart forms, the max file size (in bytes)(Default: Infinity) */
16
+ fileSize?: number;
17
+ /** For multipart forms, the max number of file fields (Default: Infinity) */
18
+ files?: number;
19
+ /** For multipart forms, the max number of parts (fields + files)(Default: Infinity) */
20
+ parts?: number;
21
+ /** For multipart forms, the max number of header key=> value pairs to parse Default: 2000(same as node's http). */
22
+ headerPairs?: number;
23
+ }
24
+
25
+ /**
26
+ * The supported HTTP methods.
27
+ */
28
+ export enum HttpMethod {
29
+ GET = 'GET',
30
+ POST = 'POST',
31
+ PUT = 'PUT',
32
+ DELETE = 'DELETE',
33
+ HEAD = 'HEAD',
34
+ OPTIONS = 'OPTIONS',
35
+ PATCH = 'PATCH'
36
+ }
37
+
38
+ /**
39
+ * Represents the current context of the request being handled.
40
+ */
41
+ export class ServiceContext {
42
+ /**
43
+ * The resolved language to be used in the current request handling.
44
+ */
45
+ public language: string;
46
+ /**
47
+ * The preferred media type to be used in the current request handling.
48
+ */
49
+ public accept: string;
50
+ /**
51
+ * The request object.
52
+ */
53
+ public request: express.Request;
54
+ /**
55
+ * The response object
56
+ */
57
+ public response: express.Response;
58
+ /**
59
+ * The next function. It can be used to delegate to the next middleware
60
+ * registered the processing of the current request.
61
+ */
62
+ public next: express.NextFunction;
63
+ }
64
+
65
+ /**
66
+ * Used to create a reference to a resource.
67
+ */
68
+ export abstract class ReferencedResource<T> {
69
+ /**
70
+ * the body to be sent
71
+ */
72
+ public body: T;
73
+
74
+ /**
75
+ * Constructor. Receives the location of the resource.
76
+ * @param location To be added to the Location header on response
77
+ * @param statusCode the response status code to be sent
78
+ */
79
+ constructor(
80
+ public location: string,
81
+ public statusCode: number
82
+ ) {}
83
+ }
84
+
85
+ /**
86
+ * The factory used to instantiate the object services
87
+ */
88
+ export interface ServiceFactory {
89
+ /**
90
+ * Create a new service object. Called before each request handling.
91
+ */
92
+ create: (serviceClass: Function, context: ServiceContext) => any;
93
+ /**
94
+ * Return the type used to handle requests to the target service.
95
+ * By default, returns the serviceClass received, but you can use this
96
+ * to implement IoC integrations, once some frameworks like typescript-ioc or
97
+ * Inversify can override constructors for injectable types.
98
+ */
99
+ getTargetClass: (serviceClass: Function) => FunctionConstructor;
100
+ }
101
+
102
+ /**
103
+ * An optional authenticator for rest services
104
+ */
105
+ export interface ServiceAuthenticator {
106
+ /**
107
+ * Get the user list of roles.
108
+ */
109
+ getRoles: (req: express.Request, res: express.Response) => Array<string>;
110
+ /**
111
+ * Initialize the authenticator
112
+ */
113
+ initialize(router: express.Router): void;
114
+ /**
115
+ * Retrieve the middleware used to authenticate users.
116
+ */
117
+ getMiddleware(): express.RequestHandler;
118
+ }
119
+
120
+ export type ServiceProcessor = (req: express.Request, res?: express.Response) => void | Promise<void>;
121
+ export type ParameterConverter = (paramValue: any) => any;
122
+
123
+ /**
124
+ * The types of parsers to parse the message body
125
+ */
126
+ export enum ParserType {
127
+ json = 'json',
128
+ text = 'text',
129
+ raw = 'raw'
130
+ }
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ import * as debug from 'debug';
4
+ import { Errors } from '../typescript-rest';
5
+ import { ParamType, ServiceProperty } from './model/metadata';
6
+ import { ParameterConverter, ServiceContext } from './model/server-types';
7
+ import { ServerContainer } from './server-container';
8
+
9
+ type ParameterContextMapper = (context: ServiceContext, property: ServiceProperty) => any;
10
+
11
+ export class ParameterProcessor {
12
+ public static get() {
13
+ return ParameterProcessor.instance;
14
+ }
15
+ private static instance = new ParameterProcessor();
16
+ private static defaultParamConverter: ParameterConverter = (p: any) => p;
17
+
18
+ private parameterMapper: Map<ParamType, ParameterContextMapper>;
19
+ private debugger = {
20
+ build: debug('typescript-rest:parameter-processor:build'),
21
+ runtime: debug('typescript-rest:parameter-processor:runtime')
22
+ };
23
+
24
+ private constructor() {
25
+ this.parameterMapper = this.initializeParameterMappers();
26
+ }
27
+
28
+ public processParameter(context: ServiceContext, property: ServiceProperty) {
29
+ const processor = this.parameterMapper.get(property.type);
30
+ if (!processor) {
31
+ throw new Errors.BadRequestError('Invalid parameter type');
32
+ }
33
+ return processor(context, property);
34
+ }
35
+
36
+ private initializeParameterMappers() {
37
+ this.debugger.build('Initializing parameters processors');
38
+ const parameterMapper: Map<ParamType, ParameterContextMapper> = new Map();
39
+
40
+ parameterMapper.set(ParamType.path, (context, property) =>
41
+ this.convertType(context.request.params[property.name], property.propertyType)
42
+ );
43
+ parameterMapper.set(ParamType.query, (context, property) =>
44
+ this.convertType(context.request.query[property.name] as string, property.propertyType)
45
+ );
46
+ parameterMapper.set(ParamType.header, (context, property) =>
47
+ this.convertType(context.request.header(property.name), property.propertyType)
48
+ );
49
+ parameterMapper.set(ParamType.cookie, (context, property) =>
50
+ this.convertType(context.request.cookies[property.name], property.propertyType)
51
+ );
52
+ parameterMapper.set(ParamType.body, (context, property) =>
53
+ this.convertType(context.request.body, property.propertyType)
54
+ );
55
+ parameterMapper.set(ParamType.file, (context, property) => {
56
+ this.debugger.runtime('Processing file parameter');
57
+ const files: Array<Express.Multer.File> = context.request.files
58
+ ? // @ts-expect-error will be an array
59
+ context.request.files[property.name]
60
+ : null;
61
+ if (files && files.length > 0) {
62
+ return files[0];
63
+ }
64
+ return null;
65
+ });
66
+ parameterMapper.set(ParamType.files, (context, property) => {
67
+ this.debugger.runtime('Processing files parameter');
68
+ // @ts-expect-error will be an array
69
+ return context.request.files?.[property.name];
70
+ });
71
+ parameterMapper.set(ParamType.form, (context, property) =>
72
+ this.convertType(context.request.body[property.name], property.propertyType)
73
+ );
74
+ parameterMapper.set(ParamType.param, (context, property) => {
75
+ const paramValue = context.request.body[property.name] || context.request.query[property.name];
76
+ return this.convertType(paramValue, property.propertyType);
77
+ });
78
+ parameterMapper.set(ParamType.context, (context) => context);
79
+ parameterMapper.set(ParamType.context_request, (context) => context.request);
80
+ parameterMapper.set(ParamType.context_response, (context) => context.response);
81
+ parameterMapper.set(ParamType.context_next, (context) => context.next);
82
+ parameterMapper.set(ParamType.context_accept, (context) => context.accept);
83
+ parameterMapper.set(ParamType.context_accept_language, (context) => context.language);
84
+
85
+ return parameterMapper;
86
+ }
87
+
88
+ private convertType(paramValue: string | boolean, paramType: Function): any {
89
+ const serializedType = paramType['name'];
90
+ this.debugger.runtime('Processing parameter. received type: %s, received value:', serializedType, paramValue);
91
+ switch (serializedType) {
92
+ case 'Number':
93
+ return paramValue === undefined ? paramValue : parseFloat(paramValue as string);
94
+ case 'Boolean':
95
+ return paramValue === undefined ? paramValue : paramValue === 'true' || paramValue === true;
96
+ default:
97
+ let converter = ServerContainer.get().paramConverters.get(paramType);
98
+ if (!converter) {
99
+ converter = ParameterProcessor.defaultParamConverter;
100
+ }
101
+
102
+ return converter(paramValue);
103
+ }
104
+ }
105
+ }