@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,279 @@
1
+ 'use strict';
2
+
3
+ import * as _ from 'lodash';
4
+ import 'reflect-metadata';
5
+ import { FileParam, MethodParam, ParamType, ServiceMethod } from '../server/model/metadata';
6
+ import { HttpMethod } from '../server/model/server-types';
7
+ import { ServerContainer } from '../server/server-container';
8
+
9
+ /**
10
+ * A decorator to tell the [[Server]] that a method
11
+ * should be called to process HTTP GET requests.
12
+ *
13
+ * For example:
14
+ *
15
+ * ```
16
+ * @ Path('people')
17
+ * class PeopleService {
18
+ * @ GET
19
+ * getPeople() {
20
+ * // ...
21
+ * }
22
+ * }
23
+ * ```
24
+ *
25
+ * Will create a service that listen for requests like:
26
+ *
27
+ * ```
28
+ * GET http://mydomain/people
29
+ * ```
30
+ */
31
+ export function GET(target: any, propertyKey: string) {
32
+ new MethodDecorator(HttpMethod.GET).decorateMethod(target, propertyKey);
33
+ }
34
+
35
+ /**
36
+ * A decorator to tell the [[Server]] that a method
37
+ * should be called to process HTTP POST requests.
38
+ *
39
+ * For example:
40
+ *
41
+ * ```
42
+ * @ Path('people')
43
+ * class PeopleService {
44
+ * @ POST
45
+ * addPerson() {
46
+ * // ...
47
+ * }
48
+ * }
49
+ * ```
50
+ *
51
+ * Will create a service that listen for requests like:
52
+ *
53
+ * ```
54
+ * POST http://mydomain/people
55
+ * ```
56
+ */
57
+ export function POST(target: any, propertyKey: string) {
58
+ new MethodDecorator(HttpMethod.POST).decorateMethod(target, propertyKey);
59
+ }
60
+
61
+ /**
62
+ * A decorator to tell the [[Server]] that a method
63
+ * should be called to process HTTP PUT requests.
64
+ *
65
+ * For example:
66
+ *
67
+ * ```
68
+ * @ Path('people')
69
+ * class PeopleService {
70
+ * @ PUT
71
+ * @ Path(':id')
72
+ * savePerson(person: Person) {
73
+ * // ...
74
+ * }
75
+ * }
76
+ * ```
77
+ *
78
+ * Will create a service that listen for requests like:
79
+ *
80
+ * ```
81
+ * PUT http://mydomain/people/123
82
+ * ```
83
+ */
84
+ export function PUT(target: any, propertyKey: string) {
85
+ new MethodDecorator(HttpMethod.PUT).decorateMethod(target, propertyKey);
86
+ }
87
+
88
+ /**
89
+ * A decorator to tell the [[Server]] that a method
90
+ * should be called to process HTTP DELETE requests.
91
+ *
92
+ * For example:
93
+ *
94
+ * ```
95
+ * @ Path('people')
96
+ * class PeopleService {
97
+ * @ DELETE
98
+ * @ Path(':id')
99
+ * removePerson(@ PathParam('id')id: string) {
100
+ * // ...
101
+ * }
102
+ * }
103
+ * ```
104
+ *
105
+ * Will create a service that listen for requests like:
106
+ *
107
+ * ```
108
+ * PUT http://mydomain/people/123
109
+ * ```
110
+ */
111
+ export function DELETE(target: any, propertyKey: string) {
112
+ new MethodDecorator(HttpMethod.DELETE).decorateMethod(target, propertyKey);
113
+ }
114
+
115
+ /**
116
+ * A decorator to tell the [[Server]] that a method
117
+ * should be called to process HTTP HEAD requests.
118
+ *
119
+ * For example:
120
+ *
121
+ * ```
122
+ * @ Path('people')
123
+ * class PeopleService {
124
+ * @ HEAD
125
+ * headPerson() {
126
+ * // ...
127
+ * }
128
+ * }
129
+ * ```
130
+ *
131
+ * Will create a service that listen for requests like:
132
+ *
133
+ * ```
134
+ * HEAD http://mydomain/people/123
135
+ * ```
136
+ */
137
+ export function HEAD(target: any, propertyKey: string) {
138
+ new MethodDecorator(HttpMethod.HEAD).decorateMethod(target, propertyKey);
139
+ }
140
+
141
+ /**
142
+ * A decorator to tell the [[Server]] that a method
143
+ * should be called to process HTTP OPTIONS requests.
144
+ *
145
+ * For example:
146
+ *
147
+ * ```
148
+ * @ Path('people')
149
+ * class PeopleService {
150
+ * @ OPTIONS
151
+ * optionsPerson() {
152
+ * // ...
153
+ * }
154
+ * }
155
+ * ```
156
+ *
157
+ * Will create a service that listen for requests like:
158
+ *
159
+ * ```
160
+ * OPTIONS http://mydomain/people/123
161
+ * ```
162
+ */
163
+ export function OPTIONS(target: any, propertyKey: string) {
164
+ new MethodDecorator(HttpMethod.OPTIONS).decorateMethod(target, propertyKey);
165
+ }
166
+
167
+ /**
168
+ * A decorator to tell the [[Server]] that a method
169
+ * should be called to process HTTP PATCH requests.
170
+ *
171
+ * For example:
172
+ *
173
+ * ```
174
+ * @ Path('people')
175
+ * class PeopleService {
176
+ * @ PATCH
177
+ * @ Path(':id')
178
+ * savePerson(person: Person) {
179
+ * // ...
180
+ * }
181
+ * }
182
+ * ```
183
+ *
184
+ * Will create a service that listen for requests like:
185
+ *
186
+ * ```
187
+ * PATCH http://mydomain/people/123
188
+ * ```
189
+ */
190
+ export function PATCH(target: any, propertyKey: string) {
191
+ new MethodDecorator(HttpMethod.PATCH).decorateMethod(target, propertyKey);
192
+ }
193
+
194
+ class MethodDecorator {
195
+ private static PROCESSORS = getParameterProcessors();
196
+
197
+ private httpMethod: HttpMethod;
198
+
199
+ constructor(httpMethod: HttpMethod) {
200
+ this.httpMethod = httpMethod;
201
+ }
202
+
203
+ public decorateMethod(target: Function, propertyKey: string) {
204
+ const serviceMethod: ServiceMethod = ServerContainer.get().registerServiceMethod(
205
+ target.constructor,
206
+ propertyKey
207
+ );
208
+ if (serviceMethod) {
209
+ // does not intercept constructor
210
+ if (!serviceMethod.httpMethod) {
211
+ serviceMethod.httpMethod = this.httpMethod;
212
+ this.processServiceMethod(target, propertyKey, serviceMethod);
213
+ } else if (serviceMethod.httpMethod !== this.httpMethod) {
214
+ throw new Error(
215
+ 'Method is already annotated with @' +
216
+ HttpMethod[serviceMethod.httpMethod] +
217
+ '. You can only map a method to one HTTP verb.'
218
+ );
219
+ }
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Extract metadata for rest methods
225
+ */
226
+ private processServiceMethod(target: any, propertyKey: string, serviceMethod: ServiceMethod) {
227
+ serviceMethod.name = propertyKey;
228
+ const paramTypes = Reflect.getOwnMetadata('design:paramtypes', target, propertyKey);
229
+ this.registerUndecoratedParameters(paramTypes, serviceMethod);
230
+
231
+ serviceMethod.parameters.forEach((param) => {
232
+ const processor = MethodDecorator.PROCESSORS.get(param.paramType);
233
+ if (processor) {
234
+ processor(serviceMethod, param);
235
+ }
236
+ });
237
+ }
238
+
239
+ private registerUndecoratedParameters(paramTypes: any, serviceMethod: ServiceMethod) {
240
+ while (paramTypes && paramTypes.length > serviceMethod.parameters.length) {
241
+ serviceMethod.parameters.push(
242
+ new MethodParam(null, paramTypes[serviceMethod.parameters.length], ParamType.body)
243
+ );
244
+ }
245
+ }
246
+ }
247
+
248
+ type ParamProcessor = (serviceMethod: ServiceMethod, param?: MethodParam) => void;
249
+ function getParameterProcessors() {
250
+ const result = new Map<ParamType, ParamProcessor>();
251
+ result.set(ParamType.cookie, (serviceMethod) => {
252
+ serviceMethod.mustParseCookies = true;
253
+ });
254
+ result.set(ParamType.file, (serviceMethod, param) => {
255
+ serviceMethod.files.push(new FileParam(param.name, true));
256
+ });
257
+ result.set(ParamType.files, (serviceMethod, param) => {
258
+ serviceMethod.files.push(new FileParam(param.name, false));
259
+ });
260
+ result.set(ParamType.param, (serviceMethod) => {
261
+ serviceMethod.acceptMultiTypedParam = true;
262
+ });
263
+ result.set(ParamType.form, (serviceMethod) => {
264
+ if (serviceMethod.mustParseBody) {
265
+ throw Error('Can not use form parameters with a body parameter on the same method.');
266
+ }
267
+ serviceMethod.mustParseForms = true;
268
+ });
269
+ result.set(ParamType.body, (serviceMethod) => {
270
+ if (serviceMethod.mustParseForms) {
271
+ throw Error('Can not use form parameters with a body parameter on the same method.');
272
+ }
273
+ if (serviceMethod.mustParseBody) {
274
+ throw Error('Can not use more than one body parameter on the same method.');
275
+ }
276
+ serviceMethod.mustParseBody = true;
277
+ });
278
+ return result;
279
+ }
@@ -0,0 +1,442 @@
1
+ 'use strict';
2
+
3
+ import * as _ from 'lodash';
4
+ import 'reflect-metadata';
5
+ import { MethodParam, ParamType, ServiceClass, ServiceMethod } from '../server/model/metadata';
6
+ import { ServerContainer } from '../server/server-container';
7
+
8
+ /**
9
+ * A decorator to be used on class properties or on service method arguments
10
+ * to inform that the decorated property or argument should be bound to the
11
+ * [[ServiceContext]] object associated to the current request.
12
+ *
13
+ * For example:
14
+ *
15
+ * ```
16
+ * @ Path('context')
17
+ * class TestService {
18
+ * @ Context
19
+ * context: ServiceContext;
20
+ * // ...
21
+ * }
22
+ * ```
23
+ *
24
+ * The field context on the above class will point to the current
25
+ * [[ServiceContext]] instance.
26
+ */
27
+ export function Context(...args: Array<any>) {
28
+ return new ParameterDecorator('Context').withType(ParamType.context).decorateParameterOrProperty(args);
29
+ }
30
+
31
+ /**
32
+ * A decorator to be used on class properties or on service method arguments
33
+ * to inform that the decorated property or argument should be bound to the
34
+ * the current request.
35
+ *
36
+ * For example:
37
+ *
38
+ * ```
39
+ * @ Path('context')
40
+ * class TestService {
41
+ * @ ContextRequest
42
+ * request: express.Request;
43
+ * // ...
44
+ * }
45
+ * ```
46
+ *
47
+ * The field request on the above class will point to the current
48
+ * request.
49
+ */
50
+ export function ContextRequest(...args: Array<any>) {
51
+ return new ParameterDecorator('ContextRequest')
52
+ .withType(ParamType.context_request)
53
+ .decorateParameterOrProperty(args);
54
+ }
55
+
56
+ /**
57
+ * A decorator to be used on class properties or on service method arguments
58
+ * to inform that the decorated property or argument should be bound to the
59
+ * the current response object.
60
+ *
61
+ * For example:
62
+ *
63
+ * ```
64
+ * @ Path('context')
65
+ * class TestService {
66
+ * @ ContextResponse
67
+ * response: express.Response;
68
+ * // ...
69
+ * }
70
+ * ```
71
+ *
72
+ * The field response on the above class will point to the current
73
+ * response object.
74
+ */
75
+ export function ContextResponse(...args: Array<any>) {
76
+ return new ParameterDecorator('ContextResponse')
77
+ .withType(ParamType.context_response)
78
+ .decorateParameterOrProperty(args);
79
+ }
80
+
81
+ /**
82
+ * A decorator to be used on class properties or on service method arguments
83
+ * to inform that the decorated property or argument should be bound to the
84
+ * the next function.
85
+ *
86
+ * For example:
87
+ *
88
+ * ```
89
+ * @ Path('context')
90
+ * class TestService {
91
+ * @ ContextNext
92
+ * next: express.NextFunction
93
+ * // ...
94
+ * }
95
+ * ```
96
+ *
97
+ * The next function can be used to delegate to the next registered
98
+ * middleware the current request processing.
99
+ */
100
+ export function ContextNext(...args: Array<any>) {
101
+ return new ParameterDecorator('ContextNext').withType(ParamType.context_next).decorateParameterOrProperty(args);
102
+ }
103
+
104
+ /**
105
+ * A decorator to be used on class properties or on service method arguments
106
+ * to inform that the decorated property or argument should be bound to the
107
+ * the current context language.
108
+ *
109
+ * For example:
110
+ *
111
+ * ```
112
+ * @ Path('context')
113
+ * class TestService {
114
+ * @ ContextLanguage
115
+ * language: string
116
+ * // ...
117
+ * }
118
+ * ```
119
+ */
120
+ export function ContextLanguage(...args: Array<any>) {
121
+ return new ParameterDecorator('ContextLanguage')
122
+ .withType(ParamType.context_accept_language)
123
+ .decorateParameterOrProperty(args);
124
+ }
125
+
126
+ /**
127
+ * A decorator to be used on class properties or on service method arguments
128
+ * to inform that the decorated property or argument should be bound to the
129
+ * the preferred media type for the current request.
130
+ *
131
+ * For example:
132
+ *
133
+ * ```
134
+ * @ Path('context')
135
+ * class TestService {
136
+ * @ ContextAccept
137
+ * media: string
138
+ * // ...
139
+ * }
140
+ * ```
141
+ */
142
+ export function ContextAccept(...args: Array<any>) {
143
+ return new ParameterDecorator('ContextAccept').withType(ParamType.context_accept).decorateParameterOrProperty(args);
144
+ }
145
+
146
+ /**
147
+ * Creates a mapping between a fragment of the requested path and
148
+ * a method argument.
149
+ *
150
+ * For example:
151
+ *
152
+ * ```
153
+ * @ Path('people')
154
+ * class PeopleService {
155
+ * @ GET
156
+ * @ Path(':id')
157
+ * getPerson(@ PathParam('id') id: string) {
158
+ * // ...
159
+ * }
160
+ * }
161
+ * ```
162
+ *
163
+ * Will create a service that listen for requests like:
164
+ *
165
+ * ```
166
+ * GET http://mydomain/people/123
167
+ * ```
168
+ *
169
+ * And pass 123 as the id argument on getPerson method's call.
170
+ */
171
+ export function PathParam(name: string) {
172
+ return new ParameterDecorator('PathParam')
173
+ .withType(ParamType.path)
174
+ .withName(name)
175
+ .decorateNamedParameterOrProperty();
176
+ }
177
+
178
+ /**
179
+ * Creates a mapping between a file on a multipart request and a method
180
+ * argument.
181
+ *
182
+ * For example:
183
+ *
184
+ * ```
185
+ * @ Path('people')
186
+ * class PeopleService {
187
+ * @ POST
188
+ * @ Path('id')
189
+ * addAvatar(@ PathParam('id') id: string,
190
+ * @ FileParam('avatar') file: Express.Multer.File) {
191
+ * // ...
192
+ * }
193
+ * }
194
+ * ```
195
+ *
196
+ * Will create a service that listen for requests and bind the
197
+ * file with name 'avatar' on the requested form to the file
198
+ * argument on addAvatar method's call.
199
+ */
200
+ export function FileParam(name: string) {
201
+ return new ParameterDecorator('FileParam')
202
+ .withType(ParamType.file)
203
+ .withName(name)
204
+ .decorateNamedParameterOrProperty();
205
+ }
206
+
207
+ /**
208
+ * Creates a mapping between a list of files on a multipart request and a method
209
+ * argument.
210
+ *
211
+ * For example:
212
+ *
213
+ * ```
214
+ * @ Path('people')
215
+ * class PeopleService {
216
+ * @ POST
217
+ * @ Path('id')
218
+ * addAvatar(@ PathParam('id') id: string,
219
+ * @ FilesParam('avatar[]') files: Array<Express.Multer.File>) {
220
+ * // ...
221
+ * }
222
+ * }
223
+ * ```
224
+ *
225
+ * Will create a service that listen for requests and bind the
226
+ * files with name 'avatar' on the request form to the file
227
+ * argument on addAvatar method's call.
228
+ */
229
+ export function FilesParam(name: string) {
230
+ return new ParameterDecorator('FilesParam')
231
+ .withType(ParamType.files)
232
+ .withName(name)
233
+ .decorateNamedParameterOrProperty();
234
+ }
235
+
236
+ /**
237
+ * Creates a mapping between a query parameter on request and a method
238
+ * argument.
239
+ *
240
+ * For example:
241
+ *
242
+ * ```
243
+ * @ Path('people')
244
+ * class PeopleService {
245
+ * @ GET
246
+ * getPeople(@ QueryParam('name') name: string) {
247
+ * // ...
248
+ * }
249
+ * }
250
+ * ```
251
+ *
252
+ * Will create a service that listen for requests like:
253
+ *
254
+ * ```
255
+ * GET http://mydomain/people?name=joe
256
+ * ```
257
+ *
258
+ * And pass 'joe' as the name argument on getPerson method's call.
259
+ */
260
+ export function QueryParam(name: string) {
261
+ return new ParameterDecorator('QueryParam')
262
+ .withType(ParamType.query)
263
+ .withName(name)
264
+ .decorateNamedParameterOrProperty();
265
+ }
266
+
267
+ /**
268
+ * Creates a mapping between a header on request and a method
269
+ * argument.
270
+ *
271
+ * For example:
272
+ *
273
+ * ```
274
+ * @ Path('people')
275
+ * class PeopleService {
276
+ * @ GET
277
+ * getPeople(@ HeaderParam('header') header: string) {
278
+ * // ...
279
+ * }
280
+ * }
281
+ * ```
282
+ *
283
+ * Will create a service that listen for requests and bind the
284
+ * header called 'header' to the header argument on getPerson method's call.
285
+ */
286
+ export function HeaderParam(name: string) {
287
+ return new ParameterDecorator('HeaderParam')
288
+ .withType(ParamType.header)
289
+ .withName(name)
290
+ .decorateNamedParameterOrProperty();
291
+ }
292
+
293
+ /**
294
+ * Creates a mapping between a cookie on request and a method
295
+ * argument.
296
+ *
297
+ * For example:
298
+ *
299
+ * ```
300
+ * @ Path('people')
301
+ * class PeopleService {
302
+ * @ GET
303
+ * getPeople(@ CookieParam('cookie') cookie: string) {
304
+ * // ...
305
+ * }
306
+ * }
307
+ * ```
308
+ *
309
+ * Will create a service that listen for requests and bind the
310
+ * cookie called 'cookie' to the cookie argument on getPerson method's call.
311
+ */
312
+ export function CookieParam(name: string) {
313
+ return new ParameterDecorator('CookieParam')
314
+ .withType(ParamType.cookie)
315
+ .withName(name)
316
+ .decorateNamedParameterOrProperty();
317
+ }
318
+
319
+ /**
320
+ * Creates a mapping between a form parameter on request and a method
321
+ * argument.
322
+ *
323
+ * For example:
324
+ *
325
+ * ```
326
+ * @ Path('people')
327
+ * class PeopleService {
328
+ * @ GET
329
+ * getPeople(@ FormParam('name') name: string) {
330
+ * // ...
331
+ * }
332
+ * }
333
+ * ```
334
+ *
335
+ * Will create a service that listen for requests and bind the
336
+ * request paramenter called 'name' to the name argument on getPerson
337
+ * method's call.
338
+ */
339
+ export function FormParam(name: string) {
340
+ return new ParameterDecorator('FormParam')
341
+ .withType(ParamType.form)
342
+ .withName(name)
343
+ .decorateNamedParameterOrProperty();
344
+ }
345
+
346
+ /**
347
+ * Creates a mapping between a parameter on request and a method
348
+ * argument.
349
+ *
350
+ * For example:
351
+ *
352
+ * ```
353
+ * @ Path('people')
354
+ * class PeopleService {
355
+ * @ GET
356
+ * getPeople(@ Param('name') name: string) {
357
+ * // ...
358
+ * }
359
+ * }
360
+ * ```
361
+ *
362
+ * Will create a service that listen for requests and bind the
363
+ * request paramenter called 'name' to the name argument on getPerson
364
+ * method's call. It will work to query parameters or form parameters
365
+ * received in the current request.
366
+ */
367
+ export function Param(name: string) {
368
+ return new ParameterDecorator('Param').withType(ParamType.param).withName(name).decorateNamedParameterOrProperty();
369
+ }
370
+
371
+ class ParameterDecorator {
372
+ private decorator: string;
373
+ private paramType: ParamType;
374
+ private nameRequired: boolean = false;
375
+ private name: string = null;
376
+
377
+ constructor(decorator: string) {
378
+ this.decorator = decorator;
379
+ }
380
+
381
+ public withType(paramType: ParamType) {
382
+ this.paramType = paramType;
383
+ return this;
384
+ }
385
+
386
+ public withName(name: string) {
387
+ this.nameRequired = true;
388
+ this.name = name ? name.trim() : '';
389
+ return this;
390
+ }
391
+
392
+ public decorateParameterOrProperty(args: Array<any>) {
393
+ if (!this.nameRequired || this.name) {
394
+ args = _.without(args, undefined);
395
+ if (args.length < 3 || typeof args[2] === 'undefined') {
396
+ return this.decorateProperty(args[0], args[1]);
397
+ } else if (args.length === 3 && typeof args[2] === 'number') {
398
+ return this.decorateParameter(args[0], args[1], args[2]);
399
+ }
400
+ }
401
+
402
+ throw new Error(`Invalid @${this.decorator} Decorator declaration.`);
403
+ }
404
+
405
+ public decorateNamedParameterOrProperty() {
406
+ return (...args: Array<any>) => {
407
+ return this.decorateParameterOrProperty(args);
408
+ };
409
+ }
410
+
411
+ private decorateParameter(target: Object, propertyKey: string, parameterIndex: number) {
412
+ const serviceMethod: ServiceMethod = ServerContainer.get().registerServiceMethod(
413
+ target.constructor,
414
+ propertyKey
415
+ );
416
+ if (serviceMethod) {
417
+ // does not intercept constructor
418
+ const paramTypes = Reflect.getOwnMetadata('design:paramtypes', target, propertyKey);
419
+
420
+ while (paramTypes && serviceMethod.parameters.length < paramTypes.length) {
421
+ serviceMethod.parameters.push(
422
+ new MethodParam(null, paramTypes[serviceMethod.parameters.length], ParamType.body)
423
+ );
424
+ }
425
+ serviceMethod.parameters[parameterIndex] = new MethodParam(
426
+ this.name,
427
+ paramTypes[parameterIndex],
428
+ this.paramType
429
+ );
430
+ }
431
+ }
432
+
433
+ private decorateProperty(target: Function, key: string) {
434
+ const classData: ServiceClass = ServerContainer.get().registerServiceClass(target.constructor);
435
+ const propertyType = Reflect.getMetadata('design:type', target, key);
436
+ classData.addProperty(key, {
437
+ name: this.name,
438
+ propertyType: propertyType,
439
+ type: this.paramType
440
+ });
441
+ }
442
+ }