@tramvai/papi 2.21.1 → 2.24.1
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/README.md +146 -7
- package/lib/createPapiMethod.d.ts +3 -2
- package/lib/index.d.ts +2 -0
- package/lib/index.es.js +23 -0
- package/lib/index.js +30 -0
- package/lib/types.d.ts +31 -25
- package/package.json +6 -17
- package/lib/browser.d.ts +0 -2
- package/lib/browser.js +0 -22
- package/lib/create.d.ts +0 -3
- package/lib/middlewares/body.d.ts +0 -10
- package/lib/middlewares/cookie.d.ts +0 -7
- package/lib/middlewares/error.d.ts +0 -7
- package/lib/middlewares/fillPapiState.d.ts +0 -6
- package/lib/middlewares/handler.d.ts +0 -7
- package/lib/middlewares/index.d.ts +0 -7
- package/lib/middlewares/timeout.d.ts +0 -10
- package/lib/middlewares/validate.d.ts +0 -15
- package/lib/server.d.ts +0 -5
- package/lib/server.es.js +0 -239
- package/lib/server.js +0 -256
package/README.md
CHANGED
|
@@ -5,29 +5,168 @@ sidebar_position: 4
|
|
|
5
5
|
|
|
6
6
|
# Papi
|
|
7
7
|
|
|
8
|
-
Library for creating and working with papi handlers.
|
|
8
|
+
Library for creating and working with papi handlers. More on papi feature you can find [here](features/papi/introduction.md)
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
12
12
|
You need to install `@tramvai/papi`
|
|
13
13
|
|
|
14
|
-
```bash
|
|
15
|
-
|
|
14
|
+
```bash npm2yarn
|
|
15
|
+
npm i @tramvai/papi
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Explanation
|
|
19
|
+
|
|
20
|
+
Library mostly provides strong typings to papi handlers and most of the logic of integration it to the tramvai app is done by [@tramvai/module-server](references/modules/server.md)
|
|
21
|
+
|
|
22
|
+
## Api
|
|
23
|
+
|
|
24
|
+
### createPapiMethod
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
- `path` - specifies the path of url that current papi should handle. Required when specifying papi in tramvai DI, and not used when specified through file api.
|
|
28
|
+
- `method` - specified HTTP-method that is acceptable by current papi. By default, it equals to "all"
|
|
29
|
+
- `handler` - see [handler](#handler)
|
|
30
|
+
- `deps` - any DI deps that papi may require
|
|
31
|
+
- `options` - additional options that controls how current papi works
|
|
32
|
+
- `timeout` - timeout for executing current papi. If timeout exceeded request is resolved with Execution timeout error.
|
|
33
|
+
|
|
34
|
+
#### handler
|
|
35
|
+
|
|
36
|
+
Function that handles actual request and should return. It accepts the details of the request and should return the response
|
|
37
|
+
|
|
38
|
+
Details of the request are passed with first parameter when handler is called. You can see available properties in typings when specifying papi method. It should provide most of the data that might be required to handle the request.
|
|
39
|
+
|
|
40
|
+
Additionally, though `this` passed data that bounded to papi method:
|
|
41
|
+
- `deps` - resolved deps that were specified when defining papi method. Deps are resolved with [Child DI Container](concepts/di.md#container-is-a-child), so do not use it for creating caches as it won't work.
|
|
42
|
+
- `log` - instance of [LOGGER_TOKEN](references/modules/log.md#logger_token) bounded with current papi method
|
|
43
|
+
|
|
44
|
+
### isPapiMethod
|
|
45
|
+
|
|
46
|
+
Type guard to check is passed object is papi handler
|
|
47
|
+
|
|
48
|
+
## How to
|
|
49
|
+
|
|
50
|
+
### Create simple papi
|
|
19
51
|
|
|
20
52
|
```tsx
|
|
21
53
|
import { createPapiMethod } from '@tramvai/papi';
|
|
22
54
|
|
|
23
55
|
export const papi = createPapiMethod({
|
|
56
|
+
// will handle requests to `/app/papi/my/papi` (actual url depends on setup)
|
|
24
57
|
path: '/my/papi',
|
|
25
|
-
method
|
|
26
|
-
|
|
58
|
+
// only requests with GET http method will be handled
|
|
59
|
+
method: 'get',
|
|
60
|
+
// function to return response to the client
|
|
61
|
+
async handler() {
|
|
27
62
|
return 'test';
|
|
28
63
|
},
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Use parameters of the request
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { createPapiMethod } from '@tramvai/papi';
|
|
71
|
+
|
|
72
|
+
export const papi = createPapiMethod({
|
|
73
|
+
async handler({ query, cookies }) {
|
|
74
|
+
const { a, b } = query;
|
|
75
|
+
const { testCookie } = cookie;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
testCookie,
|
|
79
|
+
a,
|
|
80
|
+
b
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Settings headers and status
|
|
87
|
+
|
|
88
|
+
It can be done with [responseManager](references/tokens/common.md#responsemanager-tokens)
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { createPapiMethod } from '@tramvai/papi';
|
|
92
|
+
|
|
93
|
+
export const papi = createPapiMethod({
|
|
94
|
+
async handler({ responseManager }) {
|
|
95
|
+
responseManager.setHeader('content-type', 'text/html');
|
|
96
|
+
responseManager.setStatus(200);
|
|
97
|
+
|
|
98
|
+
return `<html>...</html>`;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Use deps
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { createPapiMethod } from '@tramvai/papi';
|
|
107
|
+
|
|
108
|
+
export const papi = createPapiMethod({
|
|
109
|
+
async handler() {
|
|
110
|
+
const { cookieManager } = this.deps;
|
|
111
|
+
|
|
112
|
+
cookieManager.set({ name: 'b', value: 'abc', expires: 35 });
|
|
113
|
+
|
|
114
|
+
return "response";
|
|
115
|
+
},
|
|
29
116
|
deps: {
|
|
30
|
-
|
|
117
|
+
cookieManager: COOKIE_MANAGER_TOKEN,
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Use cache with deps
|
|
123
|
+
|
|
124
|
+
Deps are resolved with ChildContainer that means they are getting resolved on every request in order to provide request specific info. To use any of deps that should outlive scope of the request you should use provider that was initialized with [scope=SINGLETON](concepts/di.md#root-container)
|
|
125
|
+
|
|
126
|
+
For example, if you want to use some papi specific cache you should create new token and provide the cache instance with that token and with option `scope: Scope.SINGLETON`
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { createToken, Scope, provide } from '@tinkoff/dippy'
|
|
130
|
+
import { createApp } from '@tramvai/core';
|
|
131
|
+
import { createPapiMethod } from '@tramvai/papi';
|
|
132
|
+
import { Cache, CREATE_CACHE_TOKEN } from '@tramvai/tokens-common';
|
|
133
|
+
|
|
134
|
+
export const PAPI_CACHE_TOKEN = createToken<Cache<string>>('app papi cache');
|
|
135
|
+
|
|
136
|
+
const app = createApp({
|
|
137
|
+
// ...,
|
|
138
|
+
providers: [
|
|
139
|
+
// ...,
|
|
140
|
+
provide({
|
|
141
|
+
provide: PAPI_CACHE_TOKEN,
|
|
142
|
+
scope: Scope.SINGLETON,
|
|
143
|
+
useFactory: ({ createCache }) => {
|
|
144
|
+
return createCache('memory');
|
|
145
|
+
},
|
|
146
|
+
deps: {
|
|
147
|
+
createCache: CREATE_CACHE_TOKEN,
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
]
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
export const papi = createPapiMethod({
|
|
154
|
+
async handler({ query }) {
|
|
155
|
+
const { cacheKey } = query;
|
|
156
|
+
const { cache } = this.deps;
|
|
157
|
+
|
|
158
|
+
if (cache.has(cacheKey)) {
|
|
159
|
+
return cache.get(cacheKey);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result = heavyComputation();
|
|
163
|
+
|
|
164
|
+
cache.set(cacheKey, result);
|
|
165
|
+
|
|
166
|
+
return result;
|
|
31
167
|
},
|
|
168
|
+
deps: {
|
|
169
|
+
cache: PAPI_CACHE_TOKEN
|
|
170
|
+
}
|
|
32
171
|
});
|
|
33
172
|
```
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PapiParameters, Papi } from './types';
|
|
2
2
|
export declare const PAPI_PARAMETERS = "__papi_parameters__";
|
|
3
|
-
export declare const createPapiMethod: <
|
|
4
|
-
export declare const getPapiParameters:
|
|
3
|
+
export declare const createPapiMethod: <Result = any, Deps = any>(papi: PapiParameters<Result, Deps>) => Papi<any, any>;
|
|
4
|
+
export declare const getPapiParameters: (papi: Papi<any, any>) => import("./types").NormalizedPapiParameters<any, any>;
|
|
5
|
+
export declare const isPapiMethod: (papi: any) => papi is Papi<any, any>;
|
package/lib/index.d.ts
ADDED
package/lib/index.es.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const DEFAULT_TIMEOUT = 10000;
|
|
2
|
+
const PAPI_PARAMETERS = '__papi_parameters__';
|
|
3
|
+
const createPapiMethod = (papi) => {
|
|
4
|
+
const result = Object.assign(papi.handler, {
|
|
5
|
+
[PAPI_PARAMETERS]: {
|
|
6
|
+
...papi,
|
|
7
|
+
options: {
|
|
8
|
+
timeout: DEFAULT_TIMEOUT,
|
|
9
|
+
...papi.options,
|
|
10
|
+
},
|
|
11
|
+
method: papi.method || 'all',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
const getPapiParameters = (papi) => {
|
|
17
|
+
return papi[PAPI_PARAMETERS];
|
|
18
|
+
};
|
|
19
|
+
const isPapiMethod = (papi) => {
|
|
20
|
+
return papi && !!getPapiParameters(papi);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export { PAPI_PARAMETERS, createPapiMethod, getPapiParameters, isPapiMethod };
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
const DEFAULT_TIMEOUT = 10000;
|
|
6
|
+
const PAPI_PARAMETERS = '__papi_parameters__';
|
|
7
|
+
const createPapiMethod = (papi) => {
|
|
8
|
+
const result = Object.assign(papi.handler, {
|
|
9
|
+
[PAPI_PARAMETERS]: {
|
|
10
|
+
...papi,
|
|
11
|
+
options: {
|
|
12
|
+
timeout: DEFAULT_TIMEOUT,
|
|
13
|
+
...papi.options,
|
|
14
|
+
},
|
|
15
|
+
method: papi.method || 'all',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
20
|
+
const getPapiParameters = (papi) => {
|
|
21
|
+
return papi[PAPI_PARAMETERS];
|
|
22
|
+
};
|
|
23
|
+
const isPapiMethod = (papi) => {
|
|
24
|
+
return papi && !!getPapiParameters(papi);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
exports.PAPI_PARAMETERS = PAPI_PARAMETERS;
|
|
28
|
+
exports.createPapiMethod = createPapiMethod;
|
|
29
|
+
exports.getPapiParameters = getPapiParameters;
|
|
30
|
+
exports.isPapiMethod = isPapiMethod;
|
package/lib/types.d.ts
CHANGED
|
@@ -1,38 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { IncomingHttpHeaders } from 'http';
|
|
3
|
+
import type { ProvideDepsIterator } from '@tinkoff/dippy';
|
|
4
|
+
import type { Url } from '@tinkoff/url';
|
|
5
|
+
import type { REQUEST_MANAGER_TOKEN, RESPONSE_MANAGER_TOKEN, Logger } from '@tramvai/tokens-common';
|
|
3
6
|
import type { PAPI_PARAMETERS } from './createPapiMethod';
|
|
4
|
-
declare module 'express-serve-static-core' {
|
|
5
|
-
interface Response {
|
|
6
|
-
papiState: Record<string, any>;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
export { Request, Response };
|
|
10
|
-
export declare type Handler = ErrorRequestHandler | RequestHandler;
|
|
11
7
|
export declare type Method = 'all' | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
|
|
12
|
-
export declare type Middleware = Handler | Array<Handler> | null;
|
|
13
8
|
export interface Options {
|
|
14
9
|
/**
|
|
15
|
-
*
|
|
10
|
+
* Timeout in ms for handling request. If timeout is exceeded the response is resolved with 500 status.
|
|
11
|
+
* **Note**: handler will still continues its execution possibly leading to heavy resource consumption
|
|
12
|
+
*
|
|
13
|
+
* @default 10000
|
|
16
14
|
*/
|
|
17
|
-
|
|
15
|
+
timeout?: number;
|
|
18
16
|
}
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
export interface PapiHandlerOptions {
|
|
18
|
+
headers: IncomingHttpHeaders;
|
|
19
|
+
cookies: Record<string, string>;
|
|
20
|
+
params: Record<string, string>;
|
|
21
|
+
body: any;
|
|
22
|
+
parsedUrl: Url;
|
|
23
|
+
requestManager: typeof REQUEST_MANAGER_TOKEN;
|
|
24
|
+
responseManager: typeof RESPONSE_MANAGER_TOKEN;
|
|
25
|
+
}
|
|
26
|
+
export interface PapiHandlerContext<Deps> {
|
|
27
|
+
log: Logger;
|
|
28
|
+
deps: ProvideDepsIterator<Deps>;
|
|
29
|
+
}
|
|
30
|
+
export declare type NormalizedHandler<Result, Deps> = (this: PapiHandlerContext<Deps>, options: PapiHandlerOptions) => Result;
|
|
31
|
+
export interface PapiParameters<Result, Deps> {
|
|
32
|
+
path?: string;
|
|
26
33
|
method?: Method;
|
|
27
|
-
handler: NormalizedHandler<
|
|
34
|
+
handler: NormalizedHandler<Result, Deps>;
|
|
28
35
|
options?: Options;
|
|
29
36
|
deps?: Deps;
|
|
30
37
|
}
|
|
31
|
-
export declare type NormalizedPapiParameters<
|
|
32
|
-
handler: NormalizedHandler<
|
|
38
|
+
export declare type NormalizedPapiParameters<Result, Deps> = Required<PapiParameters<Result, Deps>> & {
|
|
39
|
+
handler: NormalizedHandler<Result, Deps>;
|
|
40
|
+
options: Required<Options>;
|
|
33
41
|
};
|
|
34
|
-
export declare type Papi<
|
|
42
|
+
export declare type Papi<Result = any, Deps = any> = NormalizedHandler<Deps, Result> & {
|
|
35
43
|
[PAPI_PARAMETERS]: NormalizedPapiParameters<Deps, Result>;
|
|
36
44
|
};
|
|
37
|
-
export declare type MiddlewareCreator = (papi: NormalizedPapiParameters<any, any>) => Middleware;
|
|
38
|
-
export declare type Chain = Array<MiddlewareCreator>;
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/papi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.1",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "lib/
|
|
6
|
-
"
|
|
7
|
-
"typings": "lib/server.d.ts",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"typings": "lib/index.d.ts",
|
|
8
7
|
"files": [
|
|
9
8
|
"lib",
|
|
10
9
|
"__migrations__"
|
|
@@ -19,22 +18,12 @@
|
|
|
19
18
|
"build-for-publish": "true"
|
|
20
19
|
},
|
|
21
20
|
"dependencies": {
|
|
21
|
+
"@tinkoff/url": "0.8.2",
|
|
22
22
|
"@tinkoff/utils": "^2.1.2",
|
|
23
|
-
"@types/connect-timeout": "^0.0.34",
|
|
24
|
-
"ajv": "^6.5.4",
|
|
25
|
-
"body-parser": "^1.18.3",
|
|
26
|
-
"connect-timeout": "^1.9.0",
|
|
27
|
-
"cookie-parser": "^1.4.5",
|
|
28
|
-
"express": "^4.17.1",
|
|
29
23
|
"tslib": "^2.0.3"
|
|
30
24
|
},
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"@types/connect-timeout": "^0.0.34",
|
|
33
|
-
"@types/cookie-parser": "^1.4.2",
|
|
34
|
-
"@types/express": "^4.17.9",
|
|
35
|
-
"supertest": "^3.3.0"
|
|
36
|
-
},
|
|
25
|
+
"devDependencies": {},
|
|
37
26
|
"gitHead": "8e826a214c87b188fc4d254cdd8f2a2b2c55f3a8",
|
|
38
|
-
"module": "lib/
|
|
27
|
+
"module": "lib/index.es.js",
|
|
39
28
|
"license": "Apache-2.0"
|
|
40
29
|
}
|
package/lib/browser.d.ts
DELETED
package/lib/browser.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const PAPI_PARAMETERS = '__papi_parameters__';
|
|
2
|
-
const createPapiMethod = (papi) => {
|
|
3
|
-
const handler = papi.handler.length > 1
|
|
4
|
-
? (deps) => {
|
|
5
|
-
return papi.handler(deps === null || deps === void 0 ? void 0 : deps.req, deps === null || deps === void 0 ? void 0 : deps.res, deps);
|
|
6
|
-
}
|
|
7
|
-
: papi.handler;
|
|
8
|
-
const result = Object.assign(handler, {
|
|
9
|
-
[PAPI_PARAMETERS]: {
|
|
10
|
-
...papi,
|
|
11
|
-
handler,
|
|
12
|
-
options: papi.options || {},
|
|
13
|
-
method: papi.method || 'all',
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
return result;
|
|
17
|
-
};
|
|
18
|
-
const getPapiParameters = (papi) => {
|
|
19
|
-
return papi[PAPI_PARAMETERS];
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export { createPapiMethod, getPapiParameters };
|
package/lib/create.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { MiddlewareCreator } from '../types';
|
|
2
|
-
export interface Options {
|
|
3
|
-
limit?: string | number;
|
|
4
|
-
}
|
|
5
|
-
declare module '../types' {
|
|
6
|
-
interface Options {
|
|
7
|
-
disableBodyCookie?: boolean;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
export declare const body: (options?: Options | undefined) => MiddlewareCreator;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { Request, Response } from 'express';
|
|
2
|
-
import type { MiddlewareCreator, PapiParameters } from '../types';
|
|
3
|
-
export interface Options {
|
|
4
|
-
fillState: (papi: PapiParameters<any, any>) => (req: Request, res: Response) => any;
|
|
5
|
-
}
|
|
6
|
-
export declare const fillPapiState: ({ fillState }: Options) => MiddlewareCreator;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { body } from './body';
|
|
2
|
-
export { cookie } from './cookie';
|
|
3
|
-
export { handler } from './handler';
|
|
4
|
-
export { error } from './error';
|
|
5
|
-
export { timeout } from './timeout';
|
|
6
|
-
export { validate } from './validate';
|
|
7
|
-
export { fillPapiState } from './fillPapiState';
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { MiddlewareCreator } from '../types';
|
|
2
|
-
export interface Options {
|
|
3
|
-
timeout?: string;
|
|
4
|
-
}
|
|
5
|
-
declare module '../types' {
|
|
6
|
-
interface Options {
|
|
7
|
-
timeout?: string;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
export declare const timeout: (options?: Options | undefined) => MiddlewareCreator;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import Ajv from 'ajv';
|
|
2
|
-
import type { MiddlewareCreator } from '../types';
|
|
3
|
-
export interface Options {
|
|
4
|
-
validator?: Ajv.Ajv;
|
|
5
|
-
}
|
|
6
|
-
declare module '../types' {
|
|
7
|
-
interface Options {
|
|
8
|
-
schema?: {
|
|
9
|
-
params?: {};
|
|
10
|
-
query?: {};
|
|
11
|
-
body?: {};
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
export declare const validate: (options?: Options | undefined) => MiddlewareCreator;
|
package/lib/server.d.ts
DELETED
package/lib/server.es.js
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import bodyParser from 'body-parser';
|
|
2
|
-
import cookieParser from 'cookie-parser';
|
|
3
|
-
import isNumber from '@tinkoff/utils/is/number';
|
|
4
|
-
import connectTimeout from 'connect-timeout';
|
|
5
|
-
import Ajv from 'ajv';
|
|
6
|
-
import flatten from '@tinkoff/utils/array/flatten';
|
|
7
|
-
import isFunction from '@tinkoff/utils/is/function';
|
|
8
|
-
import { Router } from 'express';
|
|
9
|
-
|
|
10
|
-
const body = (options) => {
|
|
11
|
-
var _a;
|
|
12
|
-
// PF-12590
|
|
13
|
-
const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : '2mb';
|
|
14
|
-
return (papi) => {
|
|
15
|
-
if (papi.options.disableBodyCookie) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
return [
|
|
19
|
-
bodyParser.text({ limit }),
|
|
20
|
-
bodyParser.urlencoded({
|
|
21
|
-
limit,
|
|
22
|
-
extended: true,
|
|
23
|
-
}),
|
|
24
|
-
bodyParser.json({
|
|
25
|
-
limit,
|
|
26
|
-
type: ['application/json', 'application/csp-report'],
|
|
27
|
-
}),
|
|
28
|
-
];
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const cookie = () => {
|
|
33
|
-
return (papi) => {
|
|
34
|
-
if (papi.options.disableBodyCookie) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
return cookieParser();
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const handler = () => {
|
|
42
|
-
return (papi) => {
|
|
43
|
-
return async (req, res, next) => {
|
|
44
|
-
try {
|
|
45
|
-
const payload = await papi.handler(res.papiState);
|
|
46
|
-
// Do nothing if response already has been sent (i.e. timeout error)
|
|
47
|
-
if (papi.options.respond === false || res.headersSent) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
res.json({
|
|
51
|
-
resultCode: 'OK',
|
|
52
|
-
payload,
|
|
53
|
-
});
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
catch (err) {
|
|
57
|
-
next(err);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
next();
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const error = ({ logger } = {}) => {
|
|
66
|
-
return (papi) => {
|
|
67
|
-
const log = logger === null || logger === void 0 ? void 0 : logger(papi.path);
|
|
68
|
-
return (err, req, res, next) => {
|
|
69
|
-
// Do nothing if respone already has been sent (i.e. timeout error)
|
|
70
|
-
if (res.headersSent) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
log === null || log === void 0 ? void 0 : log.error({
|
|
74
|
-
error: err,
|
|
75
|
-
event: 'papiError',
|
|
76
|
-
path: papi.path,
|
|
77
|
-
method: papi.method,
|
|
78
|
-
});
|
|
79
|
-
res.status(isNumber(err.status) ? err.status : 500);
|
|
80
|
-
// TODO: We need a general application-wide error factory which will let us to
|
|
81
|
-
// decide what can be exposed in the response
|
|
82
|
-
res.json({
|
|
83
|
-
resultCode: 'INTERNAL_ERROR',
|
|
84
|
-
errorMessage: 'internal error',
|
|
85
|
-
});
|
|
86
|
-
};
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const timeout = (options) => {
|
|
91
|
-
var _a;
|
|
92
|
-
const defaultTimeout = (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : '10s';
|
|
93
|
-
return (papi) => {
|
|
94
|
-
const papiTimeout = papi.options.timeout || defaultTimeout;
|
|
95
|
-
return connectTimeout(papiTimeout);
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const KEYS = ['params', 'query', 'body'];
|
|
100
|
-
const validate = (options) => {
|
|
101
|
-
var _a;
|
|
102
|
-
const validator = (_a = options === null || options === void 0 ? void 0 : options.validator) !== null && _a !== void 0 ? _a : new Ajv({ coerceTypes: true });
|
|
103
|
-
return (papi) => {
|
|
104
|
-
const { schema } = papi.options;
|
|
105
|
-
if (!schema || !KEYS.some((key) => schema[key])) {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
return async (req, res, next) => {
|
|
109
|
-
for (const key of KEYS) {
|
|
110
|
-
const compiledValidator = schema[key]
|
|
111
|
-
? validator.compile(schema[key])
|
|
112
|
-
: () => true;
|
|
113
|
-
const data = req[key];
|
|
114
|
-
// console.log(data, schema[key], validate, validate(data));
|
|
115
|
-
if (!compiledValidator(data)) {
|
|
116
|
-
// console.log('validation failed');
|
|
117
|
-
// TODO: Should be replaced with general application-wide error factory
|
|
118
|
-
const err = new Error(`invalid ${key}`);
|
|
119
|
-
err.status = 400;
|
|
120
|
-
err.expose = true;
|
|
121
|
-
err.errors = compiledValidator.errors;
|
|
122
|
-
return next(err);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
next();
|
|
126
|
-
};
|
|
127
|
-
};
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const fillPapiState = ({ fillState }) => {
|
|
131
|
-
return (papi) => {
|
|
132
|
-
const fn = fillState(papi);
|
|
133
|
-
return async (req, res, next) => {
|
|
134
|
-
try {
|
|
135
|
-
res.papiState = await fn(req, res);
|
|
136
|
-
}
|
|
137
|
-
catch (err) {
|
|
138
|
-
next(err);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
next();
|
|
142
|
-
};
|
|
143
|
-
};
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
var index = {
|
|
147
|
-
__proto__: null,
|
|
148
|
-
body: body,
|
|
149
|
-
cookie: cookie,
|
|
150
|
-
handler: handler,
|
|
151
|
-
error: error,
|
|
152
|
-
timeout: timeout,
|
|
153
|
-
validate: validate,
|
|
154
|
-
fillPapiState: fillPapiState
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const PAPI_PARAMETERS = '__papi_parameters__';
|
|
158
|
-
const createPapiMethod = (papi) => {
|
|
159
|
-
const handler = papi.handler.length > 1
|
|
160
|
-
? (deps) => {
|
|
161
|
-
return papi.handler(deps === null || deps === void 0 ? void 0 : deps.req, deps === null || deps === void 0 ? void 0 : deps.res, deps);
|
|
162
|
-
}
|
|
163
|
-
: papi.handler;
|
|
164
|
-
const result = Object.assign(handler, {
|
|
165
|
-
[PAPI_PARAMETERS]: {
|
|
166
|
-
...papi,
|
|
167
|
-
handler,
|
|
168
|
-
options: papi.options || {},
|
|
169
|
-
method: papi.method || 'all',
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
return result;
|
|
173
|
-
};
|
|
174
|
-
const getPapiParameters = (papi) => {
|
|
175
|
-
return papi[PAPI_PARAMETERS];
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
function convert(papis) {
|
|
179
|
-
const paths = new Set();
|
|
180
|
-
const result = [];
|
|
181
|
-
for (const papi of papis) {
|
|
182
|
-
const papiParams = getPapiParameters(papi);
|
|
183
|
-
if (!papiParams) {
|
|
184
|
-
throw new Error(`papi should be created using createPapiMethod from @tramvai/papi,
|
|
185
|
-
got: ${JSON.stringify(papi)}`);
|
|
186
|
-
}
|
|
187
|
-
const key = `${papiParams.method} ${papiParams.path}`;
|
|
188
|
-
if (paths.has(key)) {
|
|
189
|
-
throw new Error(`papi: route '${key}' already registered`);
|
|
190
|
-
}
|
|
191
|
-
result.push(papiParams);
|
|
192
|
-
paths.add(key);
|
|
193
|
-
}
|
|
194
|
-
return result;
|
|
195
|
-
}
|
|
196
|
-
function create(papis, chain) {
|
|
197
|
-
const papiParams = convert(papis);
|
|
198
|
-
const router = Router({
|
|
199
|
-
strict: false,
|
|
200
|
-
});
|
|
201
|
-
router.use((req, res, next) => {
|
|
202
|
-
res.setHeader('cache-control', 'no-cache,no-store,max-age=0,must-revalidate');
|
|
203
|
-
next();
|
|
204
|
-
});
|
|
205
|
-
for (const papi of papiParams) {
|
|
206
|
-
const middlewaresMaybeArray = chain.map((createMiddleware) => createMiddleware(papi));
|
|
207
|
-
const middlewares = flatten(middlewaresMaybeArray).filter(isFunction);
|
|
208
|
-
if (papi.options.trimPapiUrl) {
|
|
209
|
-
const subRouter = Router({
|
|
210
|
-
strict: false,
|
|
211
|
-
});
|
|
212
|
-
subRouter[papi.method]('*', middlewares);
|
|
213
|
-
// express позволяет делать вложенный роутинг https://gist.github.com/zcaceres/f38b208a492e4dcd45f487638eff716c,
|
|
214
|
-
// в этом случае в `request.url` будет находиться не значение `papi.path`, а просто `/`,
|
|
215
|
-
// полный путь при необходимости можно найти в `request.baseUrl`
|
|
216
|
-
router.use(papi.path, subRouter);
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
router[papi.method](papi.path, middlewares);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
// Handles non existed routes and twice async next() calls where second call done
|
|
223
|
-
// without arguments or with 'route' as first argument
|
|
224
|
-
router.use((req, res) => {
|
|
225
|
-
// Do nothing if respone already has been sent (i.e. timeout error)
|
|
226
|
-
if (res.headersSent) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
res.status(404);
|
|
230
|
-
// Just for backward compatibility. Maybe we can remove it.
|
|
231
|
-
res.json({
|
|
232
|
-
resultCode: 'Not found',
|
|
233
|
-
payload: {},
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
return router;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export { create, createPapiMethod, getPapiParameters, index as middlewares };
|
package/lib/server.js
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var bodyParser = require('body-parser');
|
|
6
|
-
var cookieParser = require('cookie-parser');
|
|
7
|
-
var isNumber = require('@tinkoff/utils/is/number');
|
|
8
|
-
var connectTimeout = require('connect-timeout');
|
|
9
|
-
var Ajv = require('ajv');
|
|
10
|
-
var flatten = require('@tinkoff/utils/array/flatten');
|
|
11
|
-
var isFunction = require('@tinkoff/utils/is/function');
|
|
12
|
-
var express = require('express');
|
|
13
|
-
|
|
14
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
15
|
-
|
|
16
|
-
var bodyParser__default = /*#__PURE__*/_interopDefaultLegacy(bodyParser);
|
|
17
|
-
var cookieParser__default = /*#__PURE__*/_interopDefaultLegacy(cookieParser);
|
|
18
|
-
var isNumber__default = /*#__PURE__*/_interopDefaultLegacy(isNumber);
|
|
19
|
-
var connectTimeout__default = /*#__PURE__*/_interopDefaultLegacy(connectTimeout);
|
|
20
|
-
var Ajv__default = /*#__PURE__*/_interopDefaultLegacy(Ajv);
|
|
21
|
-
var flatten__default = /*#__PURE__*/_interopDefaultLegacy(flatten);
|
|
22
|
-
var isFunction__default = /*#__PURE__*/_interopDefaultLegacy(isFunction);
|
|
23
|
-
|
|
24
|
-
const body = (options) => {
|
|
25
|
-
var _a;
|
|
26
|
-
// PF-12590
|
|
27
|
-
const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : '2mb';
|
|
28
|
-
return (papi) => {
|
|
29
|
-
if (papi.options.disableBodyCookie) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
return [
|
|
33
|
-
bodyParser__default["default"].text({ limit }),
|
|
34
|
-
bodyParser__default["default"].urlencoded({
|
|
35
|
-
limit,
|
|
36
|
-
extended: true,
|
|
37
|
-
}),
|
|
38
|
-
bodyParser__default["default"].json({
|
|
39
|
-
limit,
|
|
40
|
-
type: ['application/json', 'application/csp-report'],
|
|
41
|
-
}),
|
|
42
|
-
];
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const cookie = () => {
|
|
47
|
-
return (papi) => {
|
|
48
|
-
if (papi.options.disableBodyCookie) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
return cookieParser__default["default"]();
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const handler = () => {
|
|
56
|
-
return (papi) => {
|
|
57
|
-
return async (req, res, next) => {
|
|
58
|
-
try {
|
|
59
|
-
const payload = await papi.handler(res.papiState);
|
|
60
|
-
// Do nothing if response already has been sent (i.e. timeout error)
|
|
61
|
-
if (papi.options.respond === false || res.headersSent) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
res.json({
|
|
65
|
-
resultCode: 'OK',
|
|
66
|
-
payload,
|
|
67
|
-
});
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
next(err);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
next();
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const error = ({ logger } = {}) => {
|
|
80
|
-
return (papi) => {
|
|
81
|
-
const log = logger === null || logger === void 0 ? void 0 : logger(papi.path);
|
|
82
|
-
return (err, req, res, next) => {
|
|
83
|
-
// Do nothing if respone already has been sent (i.e. timeout error)
|
|
84
|
-
if (res.headersSent) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
log === null || log === void 0 ? void 0 : log.error({
|
|
88
|
-
error: err,
|
|
89
|
-
event: 'papiError',
|
|
90
|
-
path: papi.path,
|
|
91
|
-
method: papi.method,
|
|
92
|
-
});
|
|
93
|
-
res.status(isNumber__default["default"](err.status) ? err.status : 500);
|
|
94
|
-
// TODO: We need a general application-wide error factory which will let us to
|
|
95
|
-
// decide what can be exposed in the response
|
|
96
|
-
res.json({
|
|
97
|
-
resultCode: 'INTERNAL_ERROR',
|
|
98
|
-
errorMessage: 'internal error',
|
|
99
|
-
});
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const timeout = (options) => {
|
|
105
|
-
var _a;
|
|
106
|
-
const defaultTimeout = (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : '10s';
|
|
107
|
-
return (papi) => {
|
|
108
|
-
const papiTimeout = papi.options.timeout || defaultTimeout;
|
|
109
|
-
return connectTimeout__default["default"](papiTimeout);
|
|
110
|
-
};
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const KEYS = ['params', 'query', 'body'];
|
|
114
|
-
const validate = (options) => {
|
|
115
|
-
var _a;
|
|
116
|
-
const validator = (_a = options === null || options === void 0 ? void 0 : options.validator) !== null && _a !== void 0 ? _a : new Ajv__default["default"]({ coerceTypes: true });
|
|
117
|
-
return (papi) => {
|
|
118
|
-
const { schema } = papi.options;
|
|
119
|
-
if (!schema || !KEYS.some((key) => schema[key])) {
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
return async (req, res, next) => {
|
|
123
|
-
for (const key of KEYS) {
|
|
124
|
-
const compiledValidator = schema[key]
|
|
125
|
-
? validator.compile(schema[key])
|
|
126
|
-
: () => true;
|
|
127
|
-
const data = req[key];
|
|
128
|
-
// console.log(data, schema[key], validate, validate(data));
|
|
129
|
-
if (!compiledValidator(data)) {
|
|
130
|
-
// console.log('validation failed');
|
|
131
|
-
// TODO: Should be replaced with general application-wide error factory
|
|
132
|
-
const err = new Error(`invalid ${key}`);
|
|
133
|
-
err.status = 400;
|
|
134
|
-
err.expose = true;
|
|
135
|
-
err.errors = compiledValidator.errors;
|
|
136
|
-
return next(err);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
next();
|
|
140
|
-
};
|
|
141
|
-
};
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const fillPapiState = ({ fillState }) => {
|
|
145
|
-
return (papi) => {
|
|
146
|
-
const fn = fillState(papi);
|
|
147
|
-
return async (req, res, next) => {
|
|
148
|
-
try {
|
|
149
|
-
res.papiState = await fn(req, res);
|
|
150
|
-
}
|
|
151
|
-
catch (err) {
|
|
152
|
-
next(err);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
next();
|
|
156
|
-
};
|
|
157
|
-
};
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
var index = {
|
|
161
|
-
__proto__: null,
|
|
162
|
-
body: body,
|
|
163
|
-
cookie: cookie,
|
|
164
|
-
handler: handler,
|
|
165
|
-
error: error,
|
|
166
|
-
timeout: timeout,
|
|
167
|
-
validate: validate,
|
|
168
|
-
fillPapiState: fillPapiState
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const PAPI_PARAMETERS = '__papi_parameters__';
|
|
172
|
-
const createPapiMethod = (papi) => {
|
|
173
|
-
const handler = papi.handler.length > 1
|
|
174
|
-
? (deps) => {
|
|
175
|
-
return papi.handler(deps === null || deps === void 0 ? void 0 : deps.req, deps === null || deps === void 0 ? void 0 : deps.res, deps);
|
|
176
|
-
}
|
|
177
|
-
: papi.handler;
|
|
178
|
-
const result = Object.assign(handler, {
|
|
179
|
-
[PAPI_PARAMETERS]: {
|
|
180
|
-
...papi,
|
|
181
|
-
handler,
|
|
182
|
-
options: papi.options || {},
|
|
183
|
-
method: papi.method || 'all',
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
return result;
|
|
187
|
-
};
|
|
188
|
-
const getPapiParameters = (papi) => {
|
|
189
|
-
return papi[PAPI_PARAMETERS];
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
function convert(papis) {
|
|
193
|
-
const paths = new Set();
|
|
194
|
-
const result = [];
|
|
195
|
-
for (const papi of papis) {
|
|
196
|
-
const papiParams = getPapiParameters(papi);
|
|
197
|
-
if (!papiParams) {
|
|
198
|
-
throw new Error(`papi should be created using createPapiMethod from @tramvai/papi,
|
|
199
|
-
got: ${JSON.stringify(papi)}`);
|
|
200
|
-
}
|
|
201
|
-
const key = `${papiParams.method} ${papiParams.path}`;
|
|
202
|
-
if (paths.has(key)) {
|
|
203
|
-
throw new Error(`papi: route '${key}' already registered`);
|
|
204
|
-
}
|
|
205
|
-
result.push(papiParams);
|
|
206
|
-
paths.add(key);
|
|
207
|
-
}
|
|
208
|
-
return result;
|
|
209
|
-
}
|
|
210
|
-
function create(papis, chain) {
|
|
211
|
-
const papiParams = convert(papis);
|
|
212
|
-
const router = express.Router({
|
|
213
|
-
strict: false,
|
|
214
|
-
});
|
|
215
|
-
router.use((req, res, next) => {
|
|
216
|
-
res.setHeader('cache-control', 'no-cache,no-store,max-age=0,must-revalidate');
|
|
217
|
-
next();
|
|
218
|
-
});
|
|
219
|
-
for (const papi of papiParams) {
|
|
220
|
-
const middlewaresMaybeArray = chain.map((createMiddleware) => createMiddleware(papi));
|
|
221
|
-
const middlewares = flatten__default["default"](middlewaresMaybeArray).filter(isFunction__default["default"]);
|
|
222
|
-
if (papi.options.trimPapiUrl) {
|
|
223
|
-
const subRouter = express.Router({
|
|
224
|
-
strict: false,
|
|
225
|
-
});
|
|
226
|
-
subRouter[papi.method]('*', middlewares);
|
|
227
|
-
// express позволяет делать вложенный роутинг https://gist.github.com/zcaceres/f38b208a492e4dcd45f487638eff716c,
|
|
228
|
-
// в этом случае в `request.url` будет находиться не значение `papi.path`, а просто `/`,
|
|
229
|
-
// полный путь при необходимости можно найти в `request.baseUrl`
|
|
230
|
-
router.use(papi.path, subRouter);
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
router[papi.method](papi.path, middlewares);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
// Handles non existed routes and twice async next() calls where second call done
|
|
237
|
-
// without arguments or with 'route' as first argument
|
|
238
|
-
router.use((req, res) => {
|
|
239
|
-
// Do nothing if respone already has been sent (i.e. timeout error)
|
|
240
|
-
if (res.headersSent) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
res.status(404);
|
|
244
|
-
// Just for backward compatibility. Maybe we can remove it.
|
|
245
|
-
res.json({
|
|
246
|
-
resultCode: 'Not found',
|
|
247
|
-
payload: {},
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
return router;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
exports.create = create;
|
|
254
|
-
exports.createPapiMethod = createPapiMethod;
|
|
255
|
-
exports.getPapiParameters = getPapiParameters;
|
|
256
|
-
exports.middlewares = index;
|