UNPKG

16 kBTypeScriptView Raw
1/// <reference types="koa-bodyparser" />
2/// <reference types="node" />
3import { IncomingHttpHeaders } from "http";
4import Koa, { Context, Request } from "koa";
5import { ClassReflection, MethodReflection, ParameterReflection } from "tinspector";
6export declare type HttpMethod = "post" | "get" | "put" | "delete";
7export declare type KoaMiddleware = (ctx: Context, next: () => Promise<void>) => Promise<any>;
8export declare type RequestPart = keyof Request;
9export declare type HeaderPart = keyof IncomingHttpHeaders;
10export declare type Class = new (...args: any[]) => any;
11export declare type DefaultConverter = "Boolean" | "Number" | "Date" | "Object" | "Array";
12export declare type Converters = {
13 default: {
14 [key in DefaultConverter]: ConverterFunction;
15 };
16 converters: Map<Function, ConverterFunction>;
17};
18export declare type ConverterFunction = (value: any, path: string[], expectedType: Function | Function[], converters: Converters) => any;
19export declare type TypeConverter = {
20 type: Class;
21 converter: ConverterFunction;
22};
23export declare type ValidatorFunction = (value: string, ctx: Context) => Promise<string | undefined>;
24export declare type ValidatorStore = {
25 [key: string]: ValidatorFunction;
26};
27export interface BindingDecorator {
28 type: "ParameterBinding";
29 process: (ctx: Context) => any;
30}
31export interface RouteDecorator {
32 name: "Route";
33 method: HttpMethod;
34 url?: string;
35}
36export interface IgnoreDecorator {
37 name: "Ignore";
38}
39export interface RootDecorator {
40 name: "Root";
41 url: string;
42}
43export interface MiddlewareDecorator {
44 name: "Middleware";
45 value: Middleware[];
46}
47export interface AuthDecorator {
48 type: "authorize:public" | "authorize:role";
49 value: string[];
50}
51export interface RouteInfo {
52 url: string;
53 method: HttpMethod;
54 action: MethodReflection;
55 controller: ClassReflection;
56}
57export interface ValidatorDecorator {
58 type: "ValidatorDecorator";
59 validator: ValidatorFunction | string;
60}
61export interface Invocation {
62 context: Readonly<Context>;
63 proceed(): Promise<ActionResult>;
64}
65export interface Middleware {
66 execute(invocation: Readonly<Invocation>): Promise<ActionResult>;
67}
68export interface Facility {
69 setup(app: Readonly<PlumierApplication>): Promise<void>;
70}
71export interface DependencyResolver {
72 resolve(type: (new (...args: any[]) => any)): any;
73}
74export interface BodyParserOption {
75 enableTypes?: string[];
76 encode?: string;
77 formLimit?: string;
78 jsonLimit?: string;
79 strict?: boolean;
80 detectJSON?: (ctx: Koa.Context) => boolean;
81 extendTypes?: {
82 json?: string[];
83 form?: string[];
84 text?: string[];
85 };
86 onerror?: (err: Error, ctx: Koa.Context) => void;
87}
88export interface ValidationIssue {
89 path: string[];
90 messages: string[];
91}
92export interface FileUploadInfo {
93 field: string;
94 fileName: string;
95 originalName: string;
96 mime: string;
97 size: number;
98 encoding: string;
99}
100export interface FileParser {
101 save(subDirectory?: string): Promise<FileUploadInfo[]>;
102}
103export interface Configuration {
104 mode: "debug" | "production";
105 /**
106 * Specify controller path (absolute or relative to entry point) or the controller classes array.
107 */
108 controller: string | Class[] | Class;
109 /**
110 * Set custom dependency resolver for dependency injection
111 */
112 dependencyResolver: DependencyResolver;
113 /**
114 * Define default response status for method type get/post/put/delete, default 200
115 ```
116 responseStatus: { post: 201, put: 204, delete: 204 }
117 ```
118 */
119 responseStatus?: Partial<{
120 [key in HttpMethod]: number;
121 }>;
122 /**
123 * Set custom converters for parameter binding
124 ```
125 converters: {
126 AnimalDto: (value:any, type:Function) => new AnimalDto(value)
127 }
128 ```
129 */
130 converters?: TypeConverter[];
131 /**
132 * Set custom validator
133 */
134 validator?: (value: any, metadata: ParameterReflection, context: Context, validators?: {
135 [key: string]: ValidatorFunction;
136 }) => Promise<ValidationIssue[]>;
137 /**
138 * Multi part form file parser implementation
139 */
140 fileParser?: (ctx: Context) => FileParser;
141 /**
142 * Key-value pair to store validator logic
143 */
144 validators?: ValidatorStore;
145}
146export interface PlumierConfiguration extends Configuration {
147 middleware: Middleware[];
148 facilities: Facility[];
149}
150export interface Application {
151 /**
152 * Use Koa middleware
153 ```
154 use(KoaBodyParser())
155 ```
156 * Use inline Koa middleware
157 ```
158 use(async (ctx, next) => { })
159 ```
160 */
161 use(middleware: KoaMiddleware): Application;
162 /**
163 * Use plumier middleware by class instance inherited from Middleware
164 ```
165 use(new MyMiddleware())
166 ```
167 * Use plumier middleware by inline object
168 ```
169 use({ execute: x => x.proceed()})
170 use({ execute: async x => {
171 return new ActionResult({ json: "body" }, 200)
172 })
173 ```
174 */
175 use(middleware: Middleware): Application;
176 /**
177 * Set facility (advanced configuration)
178 ```
179 set(new WebApiFacility())
180 ```
181 */
182 set(facility: Facility): Application;
183 /**
184 * Set part of configuration
185 ```
186 set({ controllerPath: "./my-controller" })
187 ```
188 * Can be specified more than one configuration
189 ```
190 set({ mode: "production", rootPath: __dirname })
191 ```
192 */
193 set(config: Partial<Configuration>): Application;
194 /**
195 * Initialize Plumier app and return Koa application
196 ```
197 app.initialize().then(koa => koa.listen(8000))
198 ```
199 * For testing purposes
200 ```
201 const koa = await app.initialize()
202 supertest(koa.callback())
203 ```
204 */
205 initialize(): Promise<Koa>;
206}
207export interface PlumierApplication extends Application {
208 readonly koa: Koa;
209 readonly config: Readonly<PlumierConfiguration>;
210}
211declare module "koa" {
212 interface Context {
213 route?: Readonly<RouteInfo>;
214 config: Readonly<Configuration>;
215 parameters?: any[];
216 }
217}
218export declare namespace MiddlewareUtil {
219 function fromKoa(middleware: KoaMiddleware): Middleware;
220}
221export declare class ActionResult {
222 body?: any;
223 status?: number | undefined;
224 static fromContext(ctx: Context): ActionResult;
225 private readonly headers;
226 constructor(body?: any, status?: number | undefined);
227 setHeader(key: string, value: string): this;
228 setStatus(status: number): this;
229 execute(ctx: Context): Promise<void>;
230}
231export declare class HttpStatusError extends Error {
232 status: number;
233 constructor(status: number, message?: string);
234}
235export declare class ConversionError extends HttpStatusError {
236 issues: ValidationIssue;
237 constructor(issues: ValidationIssue);
238}
239export declare class ValidationError extends HttpStatusError {
240 issues: ValidationIssue[];
241 constructor(issues: ValidationIssue[]);
242}
243export declare class DefaultDependencyResolver implements DependencyResolver {
244 resolve(type: new (...args: any[]) => any): any;
245}
246export declare namespace bind {
247 /**
248 * Bind Koa Context
249 *
250 * method(@bind.ctx() ctx:any) {}
251 *
252 * Use dot separated string to access child property
253 *
254 * method(@bind.ctx("state.user") ctx:User) {}
255 * method(@bind.ctx("request.headers.ip") ip:string) {}
256 * method(@bind.ctx("body[0].id") id:string) {}
257 *
258 * @param part part of context, use dot separator to access child property
259 */
260 function ctx(part?: string): (target: any, name: string, index: number) => void;
261 /**
262 * Bind Koa request to parameter
263 *
264 * method(@bind.request() req:Request){}
265 *
266 * If parameter provided, part of request property will be bound
267 *
268 * method(@bind.request("method") httpMethod:string){}
269 * method(@bind.request("status") status:number){}
270 *
271 * @param part part of request ex: body, method, query etc
272 */
273 function request(part?: RequestPart): (target: any, name: string, index: number) => void;
274 /**
275 * Bind request body to parameter
276 *
277 * method(@bind.body() body:AnimalDto){}
278 *
279 * If parameter provided, part of body property will be bound
280 *
281 * method(@bind.body("name") name:string){}
282 * method(@bind.body("age") age:number){}
283 */
284 function body(part?: string): (target: any, name: string, index: number) => void;
285 /**
286 * Bind request header to parameter
287 *
288 * method(@bind.header() header:any){}
289 *
290 * If parameter provided, part of header property will be bound
291 *
292 * method(@bind.header("accept") accept:string){}
293 * method(@bind.header("cookie") age:any){}
294 */
295 function header(key?: HeaderPart): (target: any, name: string, index: number) => void;
296 /**
297 * Bind request query object to parameter
298 *
299 * method(@bind.query() query:any){}
300 *
301 * If parameter provided, part of query property will be bound
302 *
303 * method(@bind.query("id") id:string){}
304 * method(@bind.query("type") type:string){}
305 */
306 function query(name?: string): (target: any, name: string, index: number) => void;
307 /**
308 * Bind current login user to parameter
309 *
310 * method(@bind.user() user:User){}
311 */
312 function user(): (target: any, name: string, index: number) => void;
313 /**
314 * Bind file parser for multi part file upload. This function required `FileUploadFacility`
315 ```
316 @route.post()
317 async method(@bind.file() file:FileParser){
318 const info = await file.parse()
319 }
320 ```
321 */
322 function file(): (target: any, name: string, index: number) => void;
323 /**
324 * Bind custom part of Koa context into parameter
325 * example:
326 *
327 * method(@bind.custom(ctx => ctx.request.body) user:User){}
328 * @param process callback function to process the Koa context
329 */
330 function custom(process: (ctx: Koa.Context) => any): (target: any, name: string, index: number) => void;
331}
332export declare class RouteDecoratorImpl {
333 private decorateRoute;
334 /**
335 * Mark method as POST method http handler
336 ```
337 class AnimalController{
338 @route.post()
339 method(id:number){}
340 }
341 //result: POST /animal/method?id=<number>
342 ```
343 * Override method name with absolute url
344 ```
345 class AnimalController{
346 @route.post("/beast/:id")
347 method(id:number){}
348 }
349 //result: POST /beast/:id
350 ```
351 * Override method name with relative url
352 ```
353 class AnimalController{
354 @route.post("get")
355 method(id:number){}
356 }
357 //result: POST /animal/get?id=<number>
358 ```
359 * @param url url override
360 */
361 post(url?: string): (target: any, name: string) => void;
362 /**
363 * Mark method as GET method http handler
364 ```
365 class AnimalController{
366 @route.get()
367 method(id:number){}
368 }
369 //result: GET /animal/method?id=<number>
370 ```
371 * Override method name with absolute url
372 ```
373 class AnimalController{
374 @route.get("/beast/:id")
375 method(id:number){}
376 }
377 //result: GET /beast/:id
378 ```
379 * Override method name with relative url
380 ```
381 class AnimalController{
382 @route.get("get")
383 method(id:number){}
384 }
385 //result: GET /animal/get?id=<number>
386 ```
387 * @param url url override
388 */
389 get(url?: string): (target: any, name: string) => void;
390 /**
391 * Mark method as PUT method http handler
392 ```
393 class AnimalController{
394 @route.put()
395 method(id:number){}
396 }
397 //result: PUT /animal/method?id=<number>
398 ```
399 * Override method name with absolute url
400 ```
401 class AnimalController{
402 @route.put("/beast/:id")
403 method(id:number){}
404 }
405 //result: PUT /beast/:id
406 ```
407 * Override method name with relative url
408 ```
409 class AnimalController{
410 @route.put("get")
411 method(id:number){}
412 }
413 //result: PUT /animal/get?id=<number>
414 ```
415 * @param url url override
416 */
417 put(url?: string): (target: any, name: string) => void;
418 /**
419 * Mark method as DELETE method http handler
420 ```
421 class AnimalController{
422 @route.delete()
423 method(id:number){}
424 }
425 //result: DELETE /animal/method?id=<number>
426 ```
427 * Override method name with absolute url
428 ```
429 class AnimalController{
430 @route.delete("/beast/:id")
431 method(id:number){}
432 }
433 //result: DELETE /beast/:id
434 ```
435 * Override method name with relative url
436 ```
437 class AnimalController{
438 @route.delete("get")
439 method(id:number){}
440 }
441 //result: DELETE /animal/get?id=<number>
442 ```
443 * @param url url override
444 */
445 delete(url?: string): (target: any, name: string) => void;
446 /**
447 * Override controller name on route generation
448 ```
449 @route.root("/beast")
450 class AnimalController{
451 @route.get()
452 method(id:number){}
453 }
454 //result: GET /beast/method?id=<number>
455 ```
456 * Parameterized root, useful for nested Restful resource
457 ```
458 @route.root("/beast/:type/bunny")
459 class AnimalController{
460 @route.get(":id")
461 method(type:string, id:number){}
462 }
463 //result: GET /beast/:type/bunny/:id
464 ```
465 * @param url url override
466 */
467 root(url: string): (target: any) => void;
468 /**
469 * Ignore method from route generation
470 ```
471 class AnimalController{
472 @route.get()
473 method(id:number){}
474 @route.ignore()
475 otherMethod(type:string, id:number){}
476 }
477 //result: GET /animal/method?id=<number>
478 //otherMethod not generated
479 ```
480 */
481 ignore(): (target: any, name: string) => void;
482}
483export declare const route: RouteDecoratorImpl;
484export declare namespace middleware {
485 function use(...middleware: (Middleware | KoaMiddleware)[]): (...args: any[]) => void;
486}
487export declare function domain(): (target: any) => void;
488export declare class AuthDecoratorImpl {
489 /**
490 * Authorize controller/action to public
491 */
492 public(): (...args: any[]) => void;
493 /**
494 * Authorize controller/action accessible by sepecific role
495 * @param roles List of roles allowed
496 */
497 role(...roles: string[]): (...args: any[]) => void;
498}
499export declare const authorize: AuthDecoratorImpl;
500export declare const DefaultConfiguration: Configuration;
501export declare namespace errorMessage {
502 const RouteDoesNotHaveBackingParam = "PLUM1000: Route parameters ({0}) doesn't have appropriate backing parameter";
503 const ActionDoesNotHaveTypeInfo = "PLUM1001: Parameter binding skipped because action doesn't have @route decorator";
504 const DuplicateRouteFound = "PLUM1003: Duplicate route found in {0}";
505 const ControllerPathNotFound = "PLUM1004: Controller file or directory {0} not found";
506 const ModelWithoutTypeInformation = "PLUM1005: Parameter binding skipped because {0} doesn't have @domain() decorator";
507 const ArrayWithoutTypeInformation = "PLUM1006: Parameter binding skipped because array field without @array() decorator found in ({0})";
508 const ModelNotFound = "PLUM1007: Domain model not found, no class decorated with @domain() on provided classes";
509 const ModelPathNotFound = "PLUM1007: Domain model not found, no class decorated with @domain() on path {0}";
510 const PublicNotInParameter = "PLUM1008: @authorize.public() can not be applied to parameter";
511 const UnableToInstantiateModel = "PLUM2000: Unable to instantiate model {0}. Domain model should be instantiable using default constructor";
512 const UnableToConvertValue = "Unable to convert \"{0}\" into {1}";
513 const FileSizeExceeded = "File {0} size exceeded the maximum size";
514 const NumberOfFilesExceeded = "Number of files exceeded the maximum allowed";
515}