@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.
- package/.ci/runChecks.sh +7 -0
- package/.eslintrc.js +226 -0
- package/.github/dependabot.yml +27 -0
- package/.github/workflows/publish.yml +33 -0
- package/.github/workflows/test.yml +31 -0
- package/.prettierrc +27 -0
- package/.vscode/settings.json +31 -0
- package/LICENSE +22 -0
- package/README.md +128 -0
- package/dist/decorators/methods.d.ts +165 -0
- package/dist/decorators/methods.js +263 -0
- package/dist/decorators/methods.js.map +1 -0
- package/dist/decorators/parameters.d.ts +295 -0
- package/dist/decorators/parameters.js +423 -0
- package/dist/decorators/parameters.js.map +1 -0
- package/dist/decorators/services.d.ts +199 -0
- package/dist/decorators/services.js +367 -0
- package/dist/decorators/services.js.map +1 -0
- package/dist/server/config.d.ts +4 -0
- package/dist/server/config.js +43 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/model/errors.d.ts +111 -0
- package/dist/server/model/errors.js +178 -0
- package/dist/server/model/errors.js.map +1 -0
- package/dist/server/model/metadata.d.ts +91 -0
- package/dist/server/model/metadata.js +80 -0
- package/dist/server/model/metadata.js.map +1 -0
- package/dist/server/model/return-types.d.ts +86 -0
- package/dist/server/model/return-types.js +110 -0
- package/dist/server/model/return-types.js.map +1 -0
- package/dist/server/model/server-types.d.ts +118 -0
- package/dist/server/model/server-types.js +47 -0
- package/dist/server/model/server-types.js.map +1 -0
- package/dist/server/parameter-processor.d.ts +13 -0
- package/dist/server/parameter-processor.js +84 -0
- package/dist/server/parameter-processor.js.map +1 -0
- package/dist/server/server-container.d.ts +55 -0
- package/dist/server/server-container.js +432 -0
- package/dist/server/server-container.js.map +1 -0
- package/dist/server/server.d.ts +136 -0
- package/dist/server/server.js +278 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/service-invoker.d.ts +28 -0
- package/dist/server/service-invoker.js +270 -0
- package/dist/server/service-invoker.js.map +1 -0
- package/dist/typescript-rest.d.ts +9 -0
- package/dist/typescript-rest.js +31 -0
- package/dist/typescript-rest.js.map +1 -0
- package/package.json +113 -0
- package/src/decorators/methods.ts +279 -0
- package/src/decorators/parameters.ts +442 -0
- package/src/decorators/services.ts +390 -0
- package/src/server/config.ts +40 -0
- package/src/server/model/errors.ts +178 -0
- package/src/server/model/metadata.ts +118 -0
- package/src/server/model/return-types.ts +109 -0
- package/src/server/model/server-types.ts +130 -0
- package/src/server/parameter-processor.ts +105 -0
- package/src/server/server-container.ts +504 -0
- package/src/server/server.ts +340 -0
- package/src/server/service-invoker.ts +266 -0
- 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
|
+
}
|