express-ext 0.1.13 → 0.1.17
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/lib/GenericSearchController.js +1 -1
- package/lib/HealthController.js +4 -4
- package/lib/LoadController.js +1 -1
- package/lib/LoadSearchController.js +1 -1
- package/lib/LogController.js +67 -28
- package/lib/LowCodeController.js +1 -1
- package/lib/SearchController.js +1 -1
- package/lib/health.js +2 -2
- package/lib/http.js +38 -0
- package/lib/index.js +3 -1
- package/lib/log.js +127 -23
- package/lib/resources.js +55 -2
- package/lib/search.js +41 -14
- package/package.json +1 -1
- package/src/GenericSearchController.ts +3 -2
- package/src/HealthController.ts +5 -5
- package/src/LoadController.ts +2 -2
- package/src/LoadSearchController.ts +3 -2
- package/src/LogController.ts +100 -34
- package/src/LowCodeController.ts +3 -2
- package/src/SearchController.ts +3 -2
- package/src/health.ts +1 -1
- package/src/http.ts +32 -0
- package/src/index.ts +18 -0
- package/src/log.ts +124 -23
- package/src/metadata.ts +6 -4
- package/src/resources.ts +56 -1
- package/src/search.ts +38 -9
package/src/LoadController.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {Request, Response} from 'express';
|
|
2
|
-
import {attrs, handleError, Log, respondModel} from './http';
|
|
2
|
+
import {attrs, handleError, Log, minimize, respondModel} from './http';
|
|
3
3
|
import {Attribute, Attributes} from './metadata';
|
|
4
4
|
import {buildAndCheckId, buildKeys} from './view';
|
|
5
5
|
|
|
@@ -48,7 +48,7 @@ export class LoadController<T, ID> {
|
|
|
48
48
|
const id = buildAndCheckId<ID>(req, res, this.keys);
|
|
49
49
|
if (id) {
|
|
50
50
|
this.view(id)
|
|
51
|
-
.then(obj => respondModel(obj, res))
|
|
51
|
+
.then(obj => respondModel(minimize(obj), res))
|
|
52
52
|
.catch(err => handleError(err, res, this.log));
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -2,7 +2,7 @@ import {Request, Response} from 'express';
|
|
|
2
2
|
import {handleError, Log} from './http';
|
|
3
3
|
import {LoadController, ViewService} from './LoadController';
|
|
4
4
|
import {Attribute, Attributes} from './metadata';
|
|
5
|
-
import {Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult} from './search';
|
|
5
|
+
import {buildArray, Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult} from './search';
|
|
6
6
|
import {getMetadataFunc} from './search_func';
|
|
7
7
|
|
|
8
8
|
export class LoadSearchController<T, ID, S extends Filter> extends LoadController<T, ID> {
|
|
@@ -12,6 +12,7 @@ export class LoadSearchController<T, ID, S extends Filter> extends LoadControlle
|
|
|
12
12
|
numbers?: string[];
|
|
13
13
|
fields?: string;
|
|
14
14
|
excluding?: string;
|
|
15
|
+
array?: string[];
|
|
15
16
|
constructor(log: Log, public find: (s: S, limit?: number, skip?: number|string, fields?: string[]) => Promise<SearchResult<T>>, viewService: ViewService<T, ID> | ((id: ID, ctx?: any) => Promise<T>), keys?: Attributes|Attribute[]|string[], config?: SearchConfig|boolean, dates?: string[], numbers?: string[]) {
|
|
16
17
|
super(log, viewService, keys);
|
|
17
18
|
this.search = this.search.bind(this);
|
|
@@ -37,7 +38,7 @@ export class LoadSearchController<T, ID, S extends Filter> extends LoadControlle
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
search(req: Request, res: Response) {
|
|
40
|
-
const s = fromRequest<S>(req, this.fields, this.excluding);
|
|
41
|
+
const s = fromRequest<S>(req, buildArray(this.array, this.fields, this.excluding));
|
|
41
42
|
const l = getParameters(s, this.config);
|
|
42
43
|
const s2 = format(s, this.dates, this.numbers);
|
|
43
44
|
this.find(s2, l.limit, l.skipOrRefId, l.fields)
|
package/src/LogController.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {Request, Response} from 'express';
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { SimpleMap } from './log';
|
|
2
3
|
|
|
3
4
|
export interface NumberMap {
|
|
4
5
|
[key: string]: number;
|
|
5
6
|
}
|
|
6
|
-
export interface
|
|
7
|
+
export interface LogConfig {
|
|
7
8
|
level?: string;
|
|
8
9
|
map?: LogMapConfig;
|
|
10
|
+
constants?: SimpleMap;
|
|
11
|
+
name?: Name;
|
|
9
12
|
}
|
|
10
13
|
export interface LogMapConfig {
|
|
11
14
|
time?: string;
|
|
@@ -17,54 +20,117 @@ export interface LogMap {
|
|
|
17
20
|
level: string;
|
|
18
21
|
msg: string;
|
|
19
22
|
}
|
|
20
|
-
export interface
|
|
23
|
+
export interface Name {
|
|
24
|
+
trace: string;
|
|
25
|
+
debug: string;
|
|
26
|
+
info: string;
|
|
27
|
+
warn: string;
|
|
28
|
+
error: string;
|
|
29
|
+
panic: string;
|
|
30
|
+
fatal: string;
|
|
31
|
+
}
|
|
32
|
+
export interface Logger {
|
|
33
|
+
name: Name;
|
|
21
34
|
level: number;
|
|
22
35
|
map: LogMap;
|
|
36
|
+
constants?: SimpleMap;
|
|
37
|
+
trace(msg: string, m?: SimpleMap, ctx?: any): void;
|
|
38
|
+
debug(msg: string, m?: SimpleMap, ctx?: any): void;
|
|
39
|
+
info(msg: string, m?: SimpleMap, ctx?: any): void;
|
|
40
|
+
warn(msg: string, m?: SimpleMap, ctx?: any): void;
|
|
41
|
+
error(msg: string, m?: SimpleMap, ctx?: any): void;
|
|
42
|
+
panic(msg: string, m?: SimpleMap, ctx?: any): void;
|
|
43
|
+
fatal(msg: string, m?: SimpleMap, ctx?: any): void;
|
|
44
|
+
isLevelEnabled(level: number): boolean;
|
|
45
|
+
isTraceEnabled(): boolean;
|
|
46
|
+
isDebugEnabled(): boolean;
|
|
47
|
+
isInfoEnabled(): boolean;
|
|
48
|
+
isWarnEnabled(): boolean;
|
|
49
|
+
isErrorEnabled(): boolean;
|
|
50
|
+
isPanicEnabled(): boolean;
|
|
51
|
+
isFatalEnabled(): boolean;
|
|
23
52
|
}
|
|
24
|
-
|
|
53
|
+
export const map: NumberMap = {
|
|
54
|
+
TRACE: -2,
|
|
55
|
+
DEBUG: -1,
|
|
56
|
+
INFO: 0,
|
|
57
|
+
WARN: 1,
|
|
58
|
+
ERROR: 2,
|
|
59
|
+
PANIC: 3,
|
|
60
|
+
FATAL: 4
|
|
61
|
+
};
|
|
25
62
|
export class LogController {
|
|
26
|
-
map
|
|
27
|
-
|
|
28
|
-
|
|
63
|
+
map: NumberMap;
|
|
64
|
+
update: (logger: Logger, obj: LogConfig, mp: NumberMap) => boolean;
|
|
65
|
+
constructor(public logger: Logger, updateL?: (logger: Logger, obj: LogConfig, mp: NumberMap) => boolean, mp?: NumberMap) {
|
|
66
|
+
this.map = (mp ? mp : map);
|
|
67
|
+
this.update = updateL ? updateL : updateLog;
|
|
29
68
|
this.config = this.config.bind(this);
|
|
30
69
|
}
|
|
31
70
|
config(req: Request, res: Response) {
|
|
32
|
-
const obj:
|
|
71
|
+
const obj: LogConfig = req.body;
|
|
33
72
|
if (!obj || obj === '') {
|
|
34
73
|
return res.status(400).end('The request body cannot be empty');
|
|
35
74
|
}
|
|
36
|
-
if (!this.logger
|
|
75
|
+
if (!this.logger) {
|
|
37
76
|
return res.status(503).end('Logger is not available');
|
|
38
77
|
}
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
let changed = false;
|
|
43
|
-
if (obj.level && typeof obj.level === 'string' && obj.level.length > 0) {
|
|
44
|
-
const lv = this.map[obj.level.toUpperCase()];
|
|
45
|
-
if (lv !== undefined) {
|
|
46
|
-
this.logger.level = lv;
|
|
47
|
-
changed = true;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (obj.map) {
|
|
51
|
-
if (obj.map.level && typeof obj.map.level === 'string' && obj.map.level.length > 0) {
|
|
52
|
-
this.logger.map.level = obj.map.level;
|
|
53
|
-
changed = true;
|
|
54
|
-
}
|
|
55
|
-
if (obj.map.time && typeof obj.map.time === 'string' && obj.map.time.length > 0) {
|
|
56
|
-
this.logger.map.time = obj.map.time;
|
|
57
|
-
changed = true;
|
|
58
|
-
}
|
|
59
|
-
if (obj.map.msg && typeof obj.map.msg === 'string' && obj.map.msg.length > 0) {
|
|
60
|
-
this.logger.map.msg = obj.map.msg;
|
|
61
|
-
changed = true;
|
|
78
|
+
if (typeof obj.level === 'string' && obj.level.length > 0) {
|
|
79
|
+
if (!this.map) {
|
|
80
|
+
return res.status(503).end('Map is not available');
|
|
62
81
|
}
|
|
63
82
|
}
|
|
83
|
+
const changed = this.update(this.logger, obj, this.map);
|
|
64
84
|
if (changed) {
|
|
65
|
-
return res.status(200).
|
|
85
|
+
return res.status(200).json(true).end();
|
|
66
86
|
} else {
|
|
67
|
-
return res.status(204).
|
|
87
|
+
return res.status(204).json(false).end();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function updateLog(logger: Logger, obj: LogConfig, mp: NumberMap): boolean {
|
|
92
|
+
let changed = false;
|
|
93
|
+
if (typeof obj.level === 'string' && obj.level.length > 0) {
|
|
94
|
+
const lv = mp[obj.level.toUpperCase()];
|
|
95
|
+
if (lv !== undefined) {
|
|
96
|
+
logger.level = lv;
|
|
97
|
+
changed = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (obj.map) {
|
|
101
|
+
if (typeof obj.map.level === 'string' && obj.map.level.length > 0) {
|
|
102
|
+
logger.map.level = obj.map.level;
|
|
103
|
+
changed = true;
|
|
104
|
+
}
|
|
105
|
+
if (typeof obj.map.time === 'string' && obj.map.time.length > 0) {
|
|
106
|
+
logger.map.time = obj.map.time;
|
|
107
|
+
changed = true;
|
|
108
|
+
}
|
|
109
|
+
if (typeof obj.map.msg === 'string' && obj.map.msg.length > 0) {
|
|
110
|
+
logger.map.msg = obj.map.msg;
|
|
111
|
+
changed = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (obj.constants !== undefined && typeof obj.constants === 'object') {
|
|
115
|
+
const ks = Object.keys(obj.constants);
|
|
116
|
+
if (ks.length > 0) {
|
|
117
|
+
logger.constants = obj.constants;
|
|
118
|
+
} else {
|
|
119
|
+
logger.constants = undefined;
|
|
120
|
+
}
|
|
121
|
+
changed = true;
|
|
122
|
+
}
|
|
123
|
+
if (obj.name) {
|
|
124
|
+
if (typeof obj.name.trace === 'string'
|
|
125
|
+
&& typeof obj.name.debug === 'string'
|
|
126
|
+
&& typeof obj.name.info === 'string'
|
|
127
|
+
&& typeof obj.name.warn === 'string'
|
|
128
|
+
&& typeof obj.name.error === 'string'
|
|
129
|
+
&& typeof obj.name.panic === 'string'
|
|
130
|
+
&& typeof obj.name.fatal === 'string') {
|
|
131
|
+
logger.name = obj.name;
|
|
132
|
+
changed = true;
|
|
68
133
|
}
|
|
69
134
|
}
|
|
135
|
+
return changed;
|
|
70
136
|
}
|
package/src/LowCodeController.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {ResultInfo, StatusConfig} from './edit';
|
|
|
3
3
|
import {GenericController, GenericService} from './GenericController';
|
|
4
4
|
import {handleError, Log} from './http';
|
|
5
5
|
import {ErrorMessage} from './metadata';
|
|
6
|
-
import {Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult} from './search';
|
|
6
|
+
import {buildArray, Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult} from './search';
|
|
7
7
|
import {getMetadataFunc} from './search_func';
|
|
8
8
|
|
|
9
9
|
export interface LowCodeConfig extends StatusConfig, SearchConfig {
|
|
@@ -18,6 +18,7 @@ export class LowCodeController<T, ID, S extends Filter> extends GenericControlle
|
|
|
18
18
|
numbers?: string[];
|
|
19
19
|
fields?: string;
|
|
20
20
|
excluding?: string;
|
|
21
|
+
array?: string[];
|
|
21
22
|
constructor(log: Log, public lowCodeService: Service<T, ID, number|ResultInfo<T>, S>, config?: LowCodeConfig, validate?: (obj: T, patch?: boolean) => Promise<ErrorMessage[]>, dates?: string[], numbers?: string[]) {
|
|
22
23
|
super(log, lowCodeService, config, validate);
|
|
23
24
|
this.search = this.search.bind(this);
|
|
@@ -37,7 +38,7 @@ export class LowCodeController<T, ID, S extends Filter> extends GenericControlle
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
search(req: Request, res: Response) {
|
|
40
|
-
const s = fromRequest<S>(req, this.fields, this.excluding);
|
|
41
|
+
const s = fromRequest<S>(req, buildArray(this.array, this.fields, this.excluding));
|
|
41
42
|
const l = getParameters(s, this.config);
|
|
42
43
|
const s2 = format(s, this.dates, this.numbers);
|
|
43
44
|
this.lowCodeService.search(s2, l.limit, l.skipOrRefId, l.fields)
|
package/src/SearchController.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {Request, Response} from 'express';
|
|
2
2
|
import {handleError, Log} from './http';
|
|
3
|
-
import {Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult} from './search';
|
|
3
|
+
import {buildArray, Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult} from './search';
|
|
4
4
|
|
|
5
5
|
export class SearchController<T, S extends Filter> {
|
|
6
6
|
config?: SearchConfig;
|
|
7
7
|
csv?: boolean;
|
|
8
8
|
fields?: string;
|
|
9
9
|
excluding?: string;
|
|
10
|
+
array?: string[];
|
|
10
11
|
constructor(protected log: Log, public find: (s: S, limit?: number, skip?: number|string, fields?: string[]) => Promise<SearchResult<T>>, config?: SearchConfig|boolean, public dates?: string[], public numbers?: string[]) {
|
|
11
12
|
this.search = this.search.bind(this);
|
|
12
13
|
if (config) {
|
|
@@ -26,7 +27,7 @@ export class SearchController<T, S extends Filter> {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
search(req: Request, res: Response) {
|
|
29
|
-
const s = fromRequest<S>(req, this.fields, this.excluding);
|
|
30
|
+
const s = fromRequest<S>(req, buildArray(this.array, this.fields, this.excluding));
|
|
30
31
|
const l = getParameters(s, this.config);
|
|
31
32
|
const s2 = format(s, this.dates, this.numbers);
|
|
32
33
|
this.find(s2, l.limit, l.skipOrRefId, l.fields)
|
package/src/health.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface HealthChecker {
|
|
|
16
16
|
check(): Promise<AnyMap>;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export async function
|
|
19
|
+
export async function health(checkers: HealthChecker[]): Promise<Health> {
|
|
20
20
|
const p: Health = { status: 'UP' };
|
|
21
21
|
const total = checkers.length - 1;
|
|
22
22
|
let count = 0;
|
package/src/http.ts
CHANGED
|
@@ -239,3 +239,35 @@ export function getDate(req: Request, name: string, d?: Date): Date|undefined {
|
|
|
239
239
|
}
|
|
240
240
|
return date;
|
|
241
241
|
}
|
|
242
|
+
const o = 'object';
|
|
243
|
+
export function minimize(obj: any): any {
|
|
244
|
+
if (!obj || typeof obj !== o) {
|
|
245
|
+
return obj;
|
|
246
|
+
}
|
|
247
|
+
const keys = Object.keys(obj);
|
|
248
|
+
for (const key of keys) {
|
|
249
|
+
const v = obj[key];
|
|
250
|
+
if (v == null) {
|
|
251
|
+
delete obj[key];
|
|
252
|
+
} else if (Array.isArray(v) && v.length > 0) {
|
|
253
|
+
const v1 = v[0];
|
|
254
|
+
if (typeof v1 === o && !(v1 instanceof Date)) {
|
|
255
|
+
for (const item of v) {
|
|
256
|
+
minimize(item);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return obj;
|
|
262
|
+
}
|
|
263
|
+
export function minimizeArray<T>(arrs: T[]): T[] {
|
|
264
|
+
if (!arrs) {
|
|
265
|
+
return arrs;
|
|
266
|
+
}
|
|
267
|
+
if (arrs.length > 0) {
|
|
268
|
+
for (const obj of arrs) {
|
|
269
|
+
minimize(obj);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return arrs;
|
|
273
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {NextFunction, Request, Response} from 'express';
|
|
1
2
|
import {GenericController} from './GenericController';
|
|
2
3
|
import {GenericSearchController} from './GenericSearchController';
|
|
3
4
|
import {HealthController} from './HealthController';
|
|
@@ -40,3 +41,20 @@ export * from './edit';
|
|
|
40
41
|
export * from './GenericController';
|
|
41
42
|
export * from './GenericSearchController';
|
|
42
43
|
export * from './LowCodeController';
|
|
44
|
+
|
|
45
|
+
export interface AccessConfig {
|
|
46
|
+
origin: string;
|
|
47
|
+
credentials: string;
|
|
48
|
+
methods: string;
|
|
49
|
+
headers: string;
|
|
50
|
+
}
|
|
51
|
+
export type AccessControlAllowConfig = AccessConfig;
|
|
52
|
+
export function allow(access: AccessConfig): (req: Request, res: Response, next: NextFunction) => void {
|
|
53
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
54
|
+
res.header('Access-Control-Allow-Origin', access.origin);
|
|
55
|
+
res.header('Access-Control-Allow-Credentials', access.credentials);
|
|
56
|
+
res.header('Access-Control-Allow-Methods', access.methods);
|
|
57
|
+
res.setHeader('Access-Control-Allow-Headers', access.headers);
|
|
58
|
+
next();
|
|
59
|
+
};
|
|
60
|
+
}
|
package/src/log.ts
CHANGED
|
@@ -2,8 +2,9 @@ import { NextFunction } from 'express';
|
|
|
2
2
|
import { ParamsDictionary, Request, Response } from 'express-serve-static-core';
|
|
3
3
|
import { ParsedQs } from 'qs';
|
|
4
4
|
import { PassThrough } from 'stream';
|
|
5
|
+
import { resources } from './resources';
|
|
5
6
|
|
|
6
|
-
export interface
|
|
7
|
+
export interface LogConf {
|
|
7
8
|
log?: boolean;
|
|
8
9
|
separate?: boolean;
|
|
9
10
|
skips?: string;
|
|
@@ -23,10 +24,10 @@ export interface MiddleLog {
|
|
|
23
24
|
status: string;
|
|
24
25
|
size: string;
|
|
25
26
|
}
|
|
26
|
-
interface SimpleMap {
|
|
27
|
+
export interface SimpleMap {
|
|
27
28
|
[key: string]: string|number|boolean|Date;
|
|
28
29
|
}
|
|
29
|
-
export function createConfig(c?:
|
|
30
|
+
export function createConfig(c?: LogConf): MiddleLog {
|
|
30
31
|
if (!c) {
|
|
31
32
|
return {skips: [], duration: 'duration', request: '', response: '', status: '', size: ''};
|
|
32
33
|
}
|
|
@@ -58,24 +59,28 @@ export function removeUrlParams(url: string): string {
|
|
|
58
59
|
const startParams = url.indexOf('?');
|
|
59
60
|
return startParams !== -1 ? url.substring(0, startParams) : url;
|
|
60
61
|
}
|
|
61
|
-
export
|
|
62
|
-
|
|
62
|
+
export interface Middleware {
|
|
63
|
+
conf: MiddleLog;
|
|
64
|
+
}
|
|
65
|
+
const o = 'OPTIONS';
|
|
66
|
+
export class MiddlewareLogger {
|
|
67
|
+
constructor(public write: (msg: string, m?: SimpleMap) => void, conf?: LogConf, public build?: (req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, m: SimpleMap) => SimpleMap) {
|
|
63
68
|
this.log = this.log.bind(this);
|
|
64
|
-
this.
|
|
69
|
+
this.conf = createConfig(conf);
|
|
65
70
|
}
|
|
66
|
-
|
|
71
|
+
conf: MiddleLog;
|
|
67
72
|
log(req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>, number>, next: NextFunction) {
|
|
68
|
-
|
|
73
|
+
const m = req.method;
|
|
74
|
+
if (m !== o && this.conf.log && !skip(this.conf.skips, req.originalUrl)) {
|
|
69
75
|
const start = process.hrtime();
|
|
70
|
-
const
|
|
71
|
-
const x = this.c.request;
|
|
76
|
+
const x = this.conf.request;
|
|
72
77
|
let r = false;
|
|
73
78
|
if (m !== 'GET' && m !== 'DELETE') {
|
|
74
79
|
r = true;
|
|
75
80
|
}
|
|
76
81
|
const msg = `${m} ${req.originalUrl}`;
|
|
77
|
-
if (this.
|
|
78
|
-
if (this.
|
|
82
|
+
if (this.conf.separate && r) {
|
|
83
|
+
if (this.conf.request.length > 0) {
|
|
79
84
|
const op: SimpleMap = {};
|
|
80
85
|
op[x] = JSON.stringify(req.body);
|
|
81
86
|
if (this.build) {
|
|
@@ -91,27 +96,27 @@ export class Logger {
|
|
|
91
96
|
res.on('finish', () => {
|
|
92
97
|
const duration = getDurationInMilliseconds(start);
|
|
93
98
|
const op: SimpleMap = {};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
op[this.c.request] = JSON.stringify(req.body);
|
|
99
|
+
if (r && !this.conf.separate && this.conf.request.length > 0) {
|
|
100
|
+
op[x] = JSON.stringify(req.body);
|
|
97
101
|
}
|
|
98
|
-
if (this.
|
|
99
|
-
const rsBody = Buffer.concat(chunks).toString();
|
|
100
|
-
op[this.
|
|
102
|
+
if (this.conf.response.length > 0) {
|
|
103
|
+
const rsBody = Buffer.concat(chunks).toString(resources.encoding);
|
|
104
|
+
op[this.conf.response] = rsBody;
|
|
101
105
|
}
|
|
102
|
-
if (this.
|
|
103
|
-
op[this.
|
|
106
|
+
if (this.conf.status.length > 0) {
|
|
107
|
+
op[this.conf.status] = res.statusCode;
|
|
104
108
|
}
|
|
105
|
-
if (this.
|
|
109
|
+
if (this.conf.size.length > 0) {
|
|
106
110
|
if ('_contentLength' in res) {
|
|
107
|
-
op[this.
|
|
111
|
+
op[this.conf.size] = (res as any)['_contentLength'];
|
|
108
112
|
} else if (res.hasHeader('content-length')) {
|
|
109
113
|
const l = res.getHeader('content-length');
|
|
110
114
|
if (typeof l === 'number' || typeof l === 'string') {
|
|
111
|
-
op[this.
|
|
115
|
+
op[this.conf.size] = l;
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
118
|
}
|
|
119
|
+
op[this.conf.duration] = duration;
|
|
115
120
|
if (this.build) {
|
|
116
121
|
const op2 = this.build(req, op);
|
|
117
122
|
this.write(msg, op2);
|
|
@@ -148,3 +153,99 @@ const getDurationInMilliseconds = (start: [number, number] | undefined) => {
|
|
|
148
153
|
const diff = process.hrtime(start);
|
|
149
154
|
return (diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS;
|
|
150
155
|
};
|
|
156
|
+
|
|
157
|
+
// tslint:disable-next-line:max-classes-per-file
|
|
158
|
+
export class MiddlewareController {
|
|
159
|
+
constructor(public logger: Middleware) {
|
|
160
|
+
this.config = this.config.bind(this);
|
|
161
|
+
}
|
|
162
|
+
config(req: Request, res: Response) {
|
|
163
|
+
const obj: MiddleLog = req.body;
|
|
164
|
+
if (!obj || (obj as any) === '') {
|
|
165
|
+
return res.status(400).end('The request body cannot be empty');
|
|
166
|
+
}
|
|
167
|
+
if (!this.logger) {
|
|
168
|
+
return res.status(503).end('Logger is not available');
|
|
169
|
+
}
|
|
170
|
+
let changed = false;
|
|
171
|
+
if (obj.log !== undefined) {
|
|
172
|
+
this.logger.conf.log = obj.log;
|
|
173
|
+
changed = true;
|
|
174
|
+
}
|
|
175
|
+
if (obj.separate !== undefined) {
|
|
176
|
+
this.logger.conf.separate = obj.separate;
|
|
177
|
+
changed = true;
|
|
178
|
+
}
|
|
179
|
+
if (Array.isArray(obj.skips)) {
|
|
180
|
+
if (isValidSkips(obj.skips)) {
|
|
181
|
+
this.logger.conf.skips = obj.skips;
|
|
182
|
+
changed = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (typeof obj.duration === 'string' && obj.duration.length > 0) {
|
|
186
|
+
this.logger.conf.duration = obj.duration;
|
|
187
|
+
changed = true;
|
|
188
|
+
}
|
|
189
|
+
if (typeof obj.request === 'string') {
|
|
190
|
+
this.logger.conf.request = obj.request;
|
|
191
|
+
changed = true;
|
|
192
|
+
}
|
|
193
|
+
if (typeof obj.response === 'string') {
|
|
194
|
+
this.logger.conf.response = obj.response;
|
|
195
|
+
changed = true;
|
|
196
|
+
}
|
|
197
|
+
if (typeof obj.status === 'string') {
|
|
198
|
+
this.logger.conf.status = obj.status;
|
|
199
|
+
changed = true;
|
|
200
|
+
}
|
|
201
|
+
if (typeof obj.size === 'string') {
|
|
202
|
+
this.logger.conf.size = obj.size;
|
|
203
|
+
changed = true;
|
|
204
|
+
}
|
|
205
|
+
if (changed) {
|
|
206
|
+
return res.status(200).json(true).end();
|
|
207
|
+
} else {
|
|
208
|
+
return res.status(204).json(false).end();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
export function isValidSkips(s: string[]): boolean {
|
|
213
|
+
for (const x of s) {
|
|
214
|
+
if (!(typeof x === 'string')) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
export function mask(s: string, start: number, end: number, replace: string): string {
|
|
221
|
+
if (start < 0) {
|
|
222
|
+
start = 0;
|
|
223
|
+
}
|
|
224
|
+
if (end < 0) {
|
|
225
|
+
end = 0;
|
|
226
|
+
}
|
|
227
|
+
const t = start + end;
|
|
228
|
+
if (t >= s.length) {
|
|
229
|
+
return replace.repeat(s.length);
|
|
230
|
+
}
|
|
231
|
+
return s.substr(0, start) + replace.repeat(s.length - t) + s.substr(s.length - end);
|
|
232
|
+
}
|
|
233
|
+
export function margin(s: string, start: number, end: number, replace: string): string {
|
|
234
|
+
if (start >= end) {
|
|
235
|
+
return '';
|
|
236
|
+
}
|
|
237
|
+
if (start < 0) {
|
|
238
|
+
start = 0;
|
|
239
|
+
}
|
|
240
|
+
if (end < 0) {
|
|
241
|
+
end = 0;
|
|
242
|
+
}
|
|
243
|
+
if (start >= s.length) {
|
|
244
|
+
return replace.repeat(s.length);
|
|
245
|
+
}
|
|
246
|
+
if (end >= s.length) {
|
|
247
|
+
return replace.repeat(start) + s.substr(start);
|
|
248
|
+
}
|
|
249
|
+
return replace.repeat(start) + s.substr(start, end - start) + replace.repeat(s.length - end);
|
|
250
|
+
}
|
|
251
|
+
export const maskMargin = margin;
|
package/src/metadata.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type Type = 'ObjectId' | 'date' | 'datetime' | 'time'
|
|
2
2
|
| 'boolean' | 'number' | 'integer' | 'string' | 'text'
|
|
3
3
|
| 'object' | 'array' | 'binary'
|
|
4
4
|
| 'primitives' | 'booleans' | 'numbers' | 'integers' | 'strings' | 'dates' | 'datetimes' | 'times';
|
|
5
|
-
export type
|
|
5
|
+
export type Format = 'currency' | 'percentage' | 'email' | 'url' | 'phone' | 'fax' | 'ipv4' | 'ipv6';
|
|
6
6
|
|
|
7
|
+
export type DataType = Type;
|
|
8
|
+
export type FormatType = Format;
|
|
7
9
|
export interface ErrorMessage {
|
|
8
10
|
field: string;
|
|
9
11
|
code: string;
|
|
@@ -17,8 +19,8 @@ export interface Model {
|
|
|
17
19
|
|
|
18
20
|
export interface Attribute {
|
|
19
21
|
name?: string;
|
|
20
|
-
type?:
|
|
21
|
-
format?:
|
|
22
|
+
type?: Type;
|
|
23
|
+
format?: Format;
|
|
22
24
|
required?: boolean;
|
|
23
25
|
key?: boolean;
|
|
24
26
|
length?: number;
|
package/src/resources.ts
CHANGED
|
@@ -1,10 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { Attributes, ErrorMessage } from './metadata';
|
|
2
4
|
|
|
3
5
|
// tslint:disable-next-line:class-name
|
|
4
6
|
export class resources {
|
|
5
7
|
static createValidator?: <T>(attributes: Attributes, allowUndefined?: boolean, max?: number) => Validator<T>;
|
|
8
|
+
static check: (obj: any, attributes: Attributes, allowUndefined?: boolean, patch?: boolean) => ErrorMessage[];
|
|
9
|
+
static encoding?: BufferEncoding = 'utf-8';
|
|
6
10
|
}
|
|
7
11
|
|
|
8
12
|
export interface Validator<T> {
|
|
9
13
|
validate(obj: T, patch?: boolean): Promise<ErrorMessage[]>;
|
|
10
14
|
}
|
|
15
|
+
|
|
16
|
+
// tslint:disable-next-line:max-classes-per-file
|
|
17
|
+
export class TypeChecker {
|
|
18
|
+
constructor(public attributes: Attributes, public allowUndefined?: boolean) {
|
|
19
|
+
this.check = this.check.bind(this);
|
|
20
|
+
}
|
|
21
|
+
check(req: Request, res: Response, next: NextFunction): void {
|
|
22
|
+
const obj = req.body;
|
|
23
|
+
if (!obj || (obj as any) === '') {
|
|
24
|
+
return res.status(400).end('The request body cannot be empty');
|
|
25
|
+
} else {
|
|
26
|
+
const errors = resources.check(obj, this.attributes, this.allowUndefined);
|
|
27
|
+
if (errors.length > 0) {
|
|
28
|
+
res.status(400).json(errors).end();
|
|
29
|
+
} else {
|
|
30
|
+
next();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export type Handler = (req: Request, res: Response, next: NextFunction) => void;
|
|
36
|
+
export function check(attributes: Attributes, allowUndefined?: boolean): Handler {
|
|
37
|
+
const x = new TypeChecker(attributes, allowUndefined);
|
|
38
|
+
return x.check;
|
|
39
|
+
}
|
|
40
|
+
export interface Template {
|
|
41
|
+
name?: string | null;
|
|
42
|
+
text: string;
|
|
43
|
+
templates: TemplateNode[];
|
|
44
|
+
}
|
|
45
|
+
export interface TemplateNode {
|
|
46
|
+
type: string;
|
|
47
|
+
text: string;
|
|
48
|
+
property: string | null;
|
|
49
|
+
encode?: string | null;
|
|
50
|
+
value: string | null;
|
|
51
|
+
}
|
|
52
|
+
export function loadTemplates(ok: boolean|undefined, buildTemplates: (streams: string[], correct?: (stream: string) => string) => Map<string, Template>, correct?: (stream: string) => string, files?: string[]): Map<string, Template>|undefined {
|
|
53
|
+
if (!ok) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
if (!files) {
|
|
57
|
+
files = ['./src/query.xml'];
|
|
58
|
+
}
|
|
59
|
+
const mappers: string[] = [];
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
const mapper = fs.readFileSync(file, 'utf8');
|
|
62
|
+
mappers.push(mapper);
|
|
63
|
+
}
|
|
64
|
+
return buildTemplates(mappers, correct);
|
|
65
|
+
}
|