express-ext 0.1.12 → 0.1.16
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/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/index.js +2 -1
- package/lib/log.js +212 -0
- package/lib/resources.js +54 -2
- package/lib/search.js +39 -13
- package/package.json +1 -1
- package/src/GenericController.ts +4 -4
- package/src/GenericSearchController.ts +5 -4
- package/src/HealthController.ts +5 -5
- package/src/LoadController.ts +2 -2
- package/src/LoadSearchController.ts +5 -4
- package/src/LogController.ts +97 -31
- package/src/LowCodeController.ts +5 -4
- package/src/SearchController.ts +5 -4
- package/src/health.ts +1 -1
- package/src/http.ts +2 -0
- package/src/index.ts +19 -0
- package/src/log.ts +217 -0
- package/src/metadata.ts +6 -4
- package/src/resources.ts +54 -1
- package/src/search.ts +36 -8
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {Request, Response} from 'express';
|
|
2
|
-
import {handleError} from './http';
|
|
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,7 +12,8 @@ export class LoadSearchController<T, ID, S extends Filter> extends LoadControlle
|
|
|
12
12
|
numbers?: string[];
|
|
13
13
|
fields?: string;
|
|
14
14
|
excluding?: string;
|
|
15
|
-
|
|
15
|
+
array?: string[];
|
|
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);
|
|
18
19
|
if (config) {
|
|
@@ -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,4 +1,5 @@
|
|
|
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;
|
|
@@ -6,6 +7,8 @@ export interface NumberMap {
|
|
|
6
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,15 +20,51 @@ export interface LogMap {
|
|
|
17
20
|
level: string;
|
|
18
21
|
msg: string;
|
|
19
22
|
}
|
|
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
|
+
}
|
|
20
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) {
|
|
@@ -33,38 +72,65 @@ export class LogController {
|
|
|
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
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {Request, Response} from 'express';
|
|
2
2
|
import {ResultInfo, StatusConfig} from './edit';
|
|
3
3
|
import {GenericController, GenericService} from './GenericController';
|
|
4
|
-
import {handleError} from './http';
|
|
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,7 +18,8 @@ export class LowCodeController<T, ID, S extends Filter> extends GenericControlle
|
|
|
18
18
|
numbers?: string[];
|
|
19
19
|
fields?: string;
|
|
20
20
|
excluding?: string;
|
|
21
|
-
|
|
21
|
+
array?: string[];
|
|
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);
|
|
24
25
|
this.config = initializeConfig(config);
|
|
@@ -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,13 +1,14 @@
|
|
|
1
1
|
import {Request, Response} from 'express';
|
|
2
|
-
import {handleError} from './http';
|
|
3
|
-
import {Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult} from './search';
|
|
2
|
+
import {handleError, Log} from './http';
|
|
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
|
-
|
|
10
|
+
array?: string[];
|
|
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) {
|
|
13
14
|
if (typeof config === 'boolean') {
|
|
@@ -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
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {Request, Response} from 'express';
|
|
2
2
|
import {Attribute} from './metadata';
|
|
3
3
|
|
|
4
|
+
export type Log = (msg: string) => void;
|
|
5
|
+
export type LogFunc = Log;
|
|
4
6
|
export function handleError(err: any, res: Response, log?: (msg: string) => void) {
|
|
5
7
|
if (log) {
|
|
6
8
|
log(toString(err));
|
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';
|
|
@@ -26,6 +27,7 @@ export {Service as LowCodeService};
|
|
|
26
27
|
export * from './health';
|
|
27
28
|
export * from './HealthController';
|
|
28
29
|
export * from './LogController';
|
|
30
|
+
export * from './log';
|
|
29
31
|
export * from './http';
|
|
30
32
|
export * from './metadata';
|
|
31
33
|
export * from './view';
|
|
@@ -39,3 +41,20 @@ export * from './edit';
|
|
|
39
41
|
export * from './GenericController';
|
|
40
42
|
export * from './GenericSearchController';
|
|
41
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
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { NextFunction } from 'express';
|
|
2
|
+
import { ParamsDictionary, Request, Response } from 'express-serve-static-core';
|
|
3
|
+
import { ParsedQs } from 'qs';
|
|
4
|
+
import { PassThrough } from 'stream';
|
|
5
|
+
|
|
6
|
+
export interface LogConf {
|
|
7
|
+
log?: boolean;
|
|
8
|
+
separate?: boolean;
|
|
9
|
+
skips?: string;
|
|
10
|
+
request?: string;
|
|
11
|
+
response?: string;
|
|
12
|
+
duration?: string;
|
|
13
|
+
status?: string;
|
|
14
|
+
size?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface MiddleLog {
|
|
17
|
+
log?: boolean;
|
|
18
|
+
separate?: boolean;
|
|
19
|
+
skips: string[];
|
|
20
|
+
duration: string;
|
|
21
|
+
request: string;
|
|
22
|
+
response: string;
|
|
23
|
+
status: string;
|
|
24
|
+
size: string;
|
|
25
|
+
}
|
|
26
|
+
export interface SimpleMap {
|
|
27
|
+
[key: string]: string|number|boolean|Date;
|
|
28
|
+
}
|
|
29
|
+
export function createConfig(c?: LogConf): MiddleLog {
|
|
30
|
+
if (!c) {
|
|
31
|
+
return {skips: [], duration: 'duration', request: '', response: '', status: '', size: ''};
|
|
32
|
+
}
|
|
33
|
+
const l: MiddleLog = {
|
|
34
|
+
log: c.log,
|
|
35
|
+
separate: c.separate,
|
|
36
|
+
skips: c.skips ? c.skips.split(',') : [],
|
|
37
|
+
duration: c.duration ? c.duration : 'duration',
|
|
38
|
+
request: c.request ? c.request : '',
|
|
39
|
+
response: c.response ? c.response : '',
|
|
40
|
+
status: c.status ? c.status : '',
|
|
41
|
+
size: c.size ? c.size : ''
|
|
42
|
+
};
|
|
43
|
+
return l;
|
|
44
|
+
}
|
|
45
|
+
export function skip(skips: string[], url: string): boolean {
|
|
46
|
+
if (skips.length === 0) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const u = removeUrlParams(url);
|
|
50
|
+
for (const s of skips) {
|
|
51
|
+
if (u.endsWith(s)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
export function removeUrlParams(url: string): string {
|
|
58
|
+
const startParams = url.indexOf('?');
|
|
59
|
+
return startParams !== -1 ? url.substring(0, startParams) : url;
|
|
60
|
+
}
|
|
61
|
+
export interface Middleware {
|
|
62
|
+
conf: MiddleLog;
|
|
63
|
+
}
|
|
64
|
+
const o = 'OPTIONS';
|
|
65
|
+
export class MiddlewareLogger {
|
|
66
|
+
constructor(public write: (msg: string, m?: SimpleMap) => void, conf?: LogConf, public build?: (req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, m: SimpleMap) => SimpleMap) {
|
|
67
|
+
this.log = this.log.bind(this);
|
|
68
|
+
this.conf = createConfig(conf);
|
|
69
|
+
}
|
|
70
|
+
conf: MiddleLog;
|
|
71
|
+
log(req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>, number>, next: NextFunction) {
|
|
72
|
+
const m = req.method;
|
|
73
|
+
if (m !== o && this.conf.log && !skip(this.conf.skips, req.originalUrl)) {
|
|
74
|
+
const start = process.hrtime();
|
|
75
|
+
const x = this.conf.request;
|
|
76
|
+
let r = false;
|
|
77
|
+
if (m !== 'GET' && m !== 'DELETE') {
|
|
78
|
+
r = true;
|
|
79
|
+
}
|
|
80
|
+
const msg = `${m} ${req.originalUrl}`;
|
|
81
|
+
if (this.conf.separate && r) {
|
|
82
|
+
if (this.conf.request.length > 0) {
|
|
83
|
+
const op: SimpleMap = {};
|
|
84
|
+
op[x] = JSON.stringify(req.body);
|
|
85
|
+
if (this.build) {
|
|
86
|
+
const op2 = this.build(req, op);
|
|
87
|
+
this.write(msg, op2);
|
|
88
|
+
} else {
|
|
89
|
+
this.write(msg, op);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const chunks: Uint8Array[] = [];
|
|
94
|
+
mapResponseBody(res, chunks);
|
|
95
|
+
res.on('finish', () => {
|
|
96
|
+
const duration = getDurationInMilliseconds(start);
|
|
97
|
+
const op: SimpleMap = {};
|
|
98
|
+
if (r && !this.conf.separate && this.conf.request.length > 0) {
|
|
99
|
+
op[x] = JSON.stringify(req.body);
|
|
100
|
+
}
|
|
101
|
+
if (this.conf.response.length > 0) {
|
|
102
|
+
const rsBody = Buffer.concat(chunks).toString();
|
|
103
|
+
op[this.conf.response] = rsBody;
|
|
104
|
+
}
|
|
105
|
+
if (this.conf.status.length > 0) {
|
|
106
|
+
op[this.conf.status] = res.statusCode;
|
|
107
|
+
}
|
|
108
|
+
if (this.conf.size.length > 0) {
|
|
109
|
+
if ('_contentLength' in res) {
|
|
110
|
+
op[this.conf.size] = (res as any)['_contentLength'];
|
|
111
|
+
} else if (res.hasHeader('content-length')) {
|
|
112
|
+
const l = res.getHeader('content-length');
|
|
113
|
+
if (typeof l === 'number' || typeof l === 'string') {
|
|
114
|
+
op[this.conf.size] = l;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
op[this.conf.duration] = duration;
|
|
119
|
+
if (this.build) {
|
|
120
|
+
const op2 = this.build(req, op);
|
|
121
|
+
this.write(msg, op2);
|
|
122
|
+
} else {
|
|
123
|
+
this.write(msg, op);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
next();
|
|
127
|
+
} else {
|
|
128
|
+
next();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const mapResponseBody = (res: Response<any, Record<string, any>, number>, chunks: Uint8Array[]) => {
|
|
133
|
+
const defaultWrite = res.write.bind(res);
|
|
134
|
+
const defaultEnd = res.end.bind(res);
|
|
135
|
+
const ps = new PassThrough();
|
|
136
|
+
|
|
137
|
+
ps.on('data', (data: any) => chunks.push(data));
|
|
138
|
+
|
|
139
|
+
(res as any).write = (...args: any) => {
|
|
140
|
+
(ps as any).write(...args);
|
|
141
|
+
(defaultWrite as any)(...args);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
(res as any).end = (...args: any) => {
|
|
145
|
+
ps.end(...args);
|
|
146
|
+
defaultEnd(...args);
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
const NS_PER_SEC = 1e9;
|
|
150
|
+
const NS_TO_MS = 1e6;
|
|
151
|
+
const getDurationInMilliseconds = (start: [number, number] | undefined) => {
|
|
152
|
+
const diff = process.hrtime(start);
|
|
153
|
+
return (diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export class MiddlewareController {
|
|
157
|
+
constructor(public logger: Middleware) {
|
|
158
|
+
this.config = this.config.bind(this);
|
|
159
|
+
}
|
|
160
|
+
config(req: Request, res: Response) {
|
|
161
|
+
const obj: MiddleLog = req.body;
|
|
162
|
+
if (!obj || (obj as any) === '') {
|
|
163
|
+
return res.status(400).end('The request body cannot be empty');
|
|
164
|
+
}
|
|
165
|
+
if (!this.logger) {
|
|
166
|
+
return res.status(503).end('Logger is not available');
|
|
167
|
+
}
|
|
168
|
+
let changed = false;
|
|
169
|
+
if (obj.log !== undefined) {
|
|
170
|
+
this.logger.conf.log = obj.log;
|
|
171
|
+
changed = true;
|
|
172
|
+
}
|
|
173
|
+
if (obj.separate !== undefined) {
|
|
174
|
+
this.logger.conf.separate = obj.separate;
|
|
175
|
+
changed = true;
|
|
176
|
+
}
|
|
177
|
+
if (Array.isArray(obj.skips)) {
|
|
178
|
+
if (isValidSkips(obj.skips)) {
|
|
179
|
+
this.logger.conf.skips = obj.skips;
|
|
180
|
+
changed = true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (typeof obj.duration === 'string' && obj.duration.length > 0) {
|
|
184
|
+
this.logger.conf.duration = obj.duration;
|
|
185
|
+
changed = true;
|
|
186
|
+
}
|
|
187
|
+
if (typeof obj.request === 'string') {
|
|
188
|
+
this.logger.conf.request = obj.request;
|
|
189
|
+
changed = true;
|
|
190
|
+
}
|
|
191
|
+
if (typeof obj.response === 'string') {
|
|
192
|
+
this.logger.conf.response = obj.response;
|
|
193
|
+
changed = true;
|
|
194
|
+
}
|
|
195
|
+
if (typeof obj.status === 'string') {
|
|
196
|
+
this.logger.conf.status = obj.status;
|
|
197
|
+
changed = true;
|
|
198
|
+
}
|
|
199
|
+
if (typeof obj.size === 'string') {
|
|
200
|
+
this.logger.conf.size = obj.size;
|
|
201
|
+
changed = true;
|
|
202
|
+
}
|
|
203
|
+
if (changed) {
|
|
204
|
+
return res.status(200).json(true).end();
|
|
205
|
+
} else {
|
|
206
|
+
return res.status(204).json(false).end();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export function isValidSkips(s: string[]): boolean {
|
|
211
|
+
for (const x of s) {
|
|
212
|
+
if (!(typeof x === 'string')) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
217
|
+
}
|
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,63 @@
|
|
|
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[];
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
export interface Validator<T> {
|
|
9
12
|
validate(obj: T, patch?: boolean): Promise<ErrorMessage[]>;
|
|
10
13
|
}
|
|
14
|
+
|
|
15
|
+
export class TypeChecker {
|
|
16
|
+
constructor(public attributes: Attributes, public allowUndefined?: boolean) {
|
|
17
|
+
this.check = this.check.bind(this);
|
|
18
|
+
}
|
|
19
|
+
check(req: Request, res: Response, next: NextFunction): void {
|
|
20
|
+
const obj = req.body;
|
|
21
|
+
if (!obj || (obj as any) === '') {
|
|
22
|
+
return res.status(400).end('The request body cannot be empty');
|
|
23
|
+
} else {
|
|
24
|
+
const errors = resources.check(obj, this.attributes, this.allowUndefined);
|
|
25
|
+
if (errors.length > 0) {
|
|
26
|
+
res.status(400).json(errors).end();
|
|
27
|
+
} else {
|
|
28
|
+
next();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export type Handler = (req: Request, res: Response, next: NextFunction) => void;
|
|
34
|
+
export function check(attributes: Attributes, allowUndefined?: boolean): Handler {
|
|
35
|
+
const x = new TypeChecker(attributes, allowUndefined);
|
|
36
|
+
return x.check;
|
|
37
|
+
}
|
|
38
|
+
export interface Template {
|
|
39
|
+
name?: string | null;
|
|
40
|
+
text: string;
|
|
41
|
+
templates: TemplateNode[];
|
|
42
|
+
}
|
|
43
|
+
export interface TemplateNode {
|
|
44
|
+
type: string;
|
|
45
|
+
text: string;
|
|
46
|
+
property: string | null;
|
|
47
|
+
encode?: string | null;
|
|
48
|
+
value: string | null;
|
|
49
|
+
}
|
|
50
|
+
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 {
|
|
51
|
+
if (!ok) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
if (!files) {
|
|
55
|
+
files = ['./src/query.xml'];
|
|
56
|
+
}
|
|
57
|
+
const mappers: string[] = [];
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const mapper = fs.readFileSync(file, 'utf8');
|
|
60
|
+
mappers.push(mapper);
|
|
61
|
+
}
|
|
62
|
+
return buildTemplates(mappers, correct);
|
|
63
|
+
}
|
package/src/search.ts
CHANGED
|
@@ -107,30 +107,58 @@ export function initializeConfig(conf?: SearchConfig): SearchConfig | undefined
|
|
|
107
107
|
}
|
|
108
108
|
return c;
|
|
109
109
|
}
|
|
110
|
-
export function fromRequest<S>(req: Request,
|
|
111
|
-
const s: any = (req.method === 'GET' ? fromUrl(req,
|
|
110
|
+
export function fromRequest<S>(req: Request, arr?: string[]): S {
|
|
111
|
+
const s: any = (req.method === 'GET' ? fromUrl(req, arr) : req.body);
|
|
112
112
|
return s;
|
|
113
113
|
}
|
|
114
|
-
export function
|
|
114
|
+
export function buildArray(arr?: string[], s0?: string, s1?: string, s2?: string): string[] {
|
|
115
|
+
const r: string[] = [];
|
|
116
|
+
if (arr && arr.length > 0) {
|
|
117
|
+
for (const a of arr) {
|
|
118
|
+
r.push(a);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (s0 && s0.length > 0) {
|
|
122
|
+
r.push(s0);
|
|
123
|
+
}
|
|
124
|
+
if (s1 && s1.length > 0) {
|
|
125
|
+
r.push(s1);
|
|
126
|
+
}
|
|
127
|
+
if (s2 && s2.length > 0) {
|
|
128
|
+
r.push(s2);
|
|
129
|
+
}
|
|
130
|
+
return r;
|
|
131
|
+
}
|
|
132
|
+
export function fromUrl<S>(req: Request, arr?: string[]): S {
|
|
133
|
+
/*
|
|
115
134
|
if (!fields || fields.length === 0) {
|
|
116
135
|
fields = 'fields';
|
|
117
136
|
}
|
|
137
|
+
*/
|
|
118
138
|
const s: any = {};
|
|
119
139
|
const obj = req.query;
|
|
120
140
|
const keys = Object.keys(obj);
|
|
121
141
|
for (const key of keys) {
|
|
122
|
-
if (key
|
|
142
|
+
if (inArray(key, arr)) {
|
|
123
143
|
const x = (obj[key] as string).split(',');
|
|
124
|
-
s
|
|
125
|
-
} else if (key === excluding) {
|
|
126
|
-
const x = (obj[key] as string).split(',');
|
|
127
|
-
s[key] = x;
|
|
144
|
+
setValue(s, key, x);
|
|
128
145
|
} else {
|
|
129
146
|
setValue(s, key, obj[key] as string);
|
|
130
147
|
}
|
|
131
148
|
}
|
|
132
149
|
return s;
|
|
133
150
|
}
|
|
151
|
+
export function inArray(s: string, arr?: string[]): boolean {
|
|
152
|
+
if (!arr || arr.length === 0) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
for (const a of arr) {
|
|
156
|
+
if (s === a) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
134
162
|
/*
|
|
135
163
|
export function setValue<T>(obj: T, path: string, value: string): void {
|
|
136
164
|
const paths = path.split('.');
|