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.
@@ -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
- constructor(log: (msg: string) => void, 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[]) {
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)
@@ -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?: NumberMap;
27
- constructor(public logger: Logger, mp?: NumberMap) {
28
- this.map = mp;
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 || !this.map) {
75
+ if (!this.logger) {
37
76
  return res.status(503).end('Logger is not available');
38
77
  }
39
- if (!this.map) {
40
- return res.status(503).end('Map is not available');
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).end('true');
85
+ return res.status(200).json(true).end();
66
86
  } else {
67
- return res.status(204).end('false');
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
  }
@@ -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
- constructor(log: (msg: string) => void, public lowCodeService: Service<T, ID, number|ResultInfo<T>, S>, config?: LowCodeConfig, validate?: (obj: T, patch?: boolean) => Promise<ErrorMessage[]>, dates?: string[], numbers?: string[]) {
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)
@@ -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
- constructor(protected log: (msg: string) => void, public find: (s: S, limit?: number, skip?: number|string, fields?: string[]) => Promise<SearchResult<T>>, config?: SearchConfig|boolean, public dates?: string[], public numbers?: string[]) {
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 check(checkers: HealthChecker[]): Promise<Health> {
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 DataType = 'ObjectId' | 'date' | 'datetime' | 'time'
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 FormatType = 'currency' | 'percentage' | 'email' | 'url' | 'phone' | 'fax' | 'ipv4' | 'ipv6';
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?: DataType;
21
- format?: FormatType;
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 {Attributes, ErrorMessage} from './metadata';
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, fields?: string, excluding?: string): S {
111
- const s: any = (req.method === 'GET' ? fromUrl(req, fields, excluding) : req.body);
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 fromUrl<S>(req: Request, fields?: string, excluding?: string): S {
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 === fields) {
142
+ if (inArray(key, arr)) {
123
143
  const x = (obj[key] as string).split(',');
124
- s[key] = x;
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('.');