core-express 0.1.0 → 0.1.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 +1 -1
- package/lib/GenericController.js +224 -131
- package/lib/GenericSearchController.js +58 -37
- package/lib/HealthController.js +16 -58
- package/lib/LoadController.js +71 -46
- package/lib/LoadSearchController.js +113 -41
- package/lib/LogController.js +89 -0
- package/lib/LowCodeController.js +100 -0
- package/lib/SearchController.js +32 -25
- package/lib/access.js +34 -0
- package/lib/client.js +80 -0
- package/lib/edit.js +99 -84
- package/lib/health.js +157 -76
- package/lib/http.js +316 -0
- package/lib/index.js +488 -16
- package/lib/log.js +242 -0
- package/lib/resources.js +106 -6
- package/lib/search.js +766 -160
- package/lib/view.js +48 -34
- package/package.json +8 -4
- package/src/GenericController.ts +217 -106
- package/src/GenericSearchController.ts +38 -21
- package/src/HealthController.ts +9 -9
- package/src/LoadController.ts +70 -40
- package/src/LoadSearchController.ts +114 -21
- package/src/LogController.ts +137 -0
- package/src/LowCodeController.ts +88 -0
- package/src/SearchController.ts +28 -17
- package/src/access.ts +42 -0
- package/src/client.ts +69 -0
- package/src/edit.ts +93 -61
- package/src/health.ts +27 -28
- package/src/http.ts +295 -0
- package/src/index.ts +464 -13
- package/src/log.ts +245 -0
- package/src/metadata.ts +34 -23
- package/src/resources.ts +143 -4
- package/src/search.ts +782 -198
- package/src/view.ts +44 -30
- package/tsconfig.json +1 -0
- package/lib/response.js +0 -12
- package/src/response.ts +0 -10
package/src/LoadController.ts
CHANGED
|
@@ -1,65 +1,95 @@
|
|
|
1
|
-
import {Request, Response} from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { Request, Response } from "express"
|
|
2
|
+
import { attrs, handleError, minimize, queryNumber, respondModel } from "./http"
|
|
3
|
+
import { Attribute, Attributes } from "./metadata"
|
|
4
|
+
import { buildAndCheckId, buildKeys } from "./view"
|
|
5
5
|
|
|
6
6
|
export interface ViewService<T, ID> {
|
|
7
|
-
metadata?():
|
|
8
|
-
load(id: ID, ctx?: any): Promise<T
|
|
7
|
+
metadata?(): Attributes | undefined
|
|
8
|
+
load(id: ID, ctx?: any): Promise<T | null>
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export type Load<T, ID> = (id: ID, ctx?: any) => Promise<T | null>
|
|
11
|
+
function getViewFunc<T, ID>(viewService: ViewService<T, ID> | Load<T, ID>): (id: ID, ctx?: any) => Promise<T | null> {
|
|
12
|
+
if (typeof viewService === "function") {
|
|
13
|
+
return viewService
|
|
13
14
|
}
|
|
14
|
-
return viewService.load
|
|
15
|
+
return viewService.load
|
|
15
16
|
}
|
|
16
|
-
function getKeysFunc<T, ID>(viewService: ViewService<T, ID> |
|
|
17
|
+
function getKeysFunc<T, ID>(viewService: ViewService<T, ID> | Load<T, ID>, keys?: Attributes | Attribute[] | string[]): Attribute[] | undefined {
|
|
17
18
|
if (keys) {
|
|
18
19
|
if (Array.isArray(keys)) {
|
|
19
20
|
if (keys.length > 0) {
|
|
20
|
-
if (typeof keys[0] ===
|
|
21
|
-
|
|
22
|
-
for (const str of keys) {
|
|
23
|
-
const attr: Attribute = {name: str as string, type: 'string'};
|
|
24
|
-
attrs.push(attr);
|
|
25
|
-
}
|
|
26
|
-
return attrs;
|
|
21
|
+
if (typeof keys[0] === "string") {
|
|
22
|
+
return attrs(keys as string[])
|
|
27
23
|
} else {
|
|
28
|
-
return keys as Attribute[]
|
|
24
|
+
return keys as Attribute[]
|
|
29
25
|
}
|
|
30
26
|
}
|
|
31
|
-
return undefined
|
|
27
|
+
return undefined
|
|
32
28
|
} else {
|
|
33
|
-
return buildKeys(keys as Attributes)
|
|
29
|
+
return buildKeys(keys as Attributes)
|
|
34
30
|
}
|
|
35
31
|
}
|
|
36
|
-
if (typeof viewService !==
|
|
37
|
-
const metadata = viewService.metadata()
|
|
32
|
+
if (typeof viewService !== "function" && viewService.metadata) {
|
|
33
|
+
const metadata = viewService.metadata()
|
|
38
34
|
if (metadata) {
|
|
39
|
-
return buildKeys(metadata
|
|
35
|
+
return buildKeys(metadata)
|
|
40
36
|
}
|
|
41
37
|
}
|
|
42
|
-
return undefined
|
|
38
|
+
return undefined
|
|
43
39
|
}
|
|
44
40
|
export class LoadController<T, ID> {
|
|
45
|
-
protected keys?: Attribute[]
|
|
46
|
-
protected view:
|
|
47
|
-
constructor(
|
|
48
|
-
this.load = this.load.bind(this)
|
|
49
|
-
this.view = getViewFunc(viewService)
|
|
50
|
-
this.keys = getKeysFunc(viewService, keys)
|
|
41
|
+
protected keys?: Attribute[]
|
|
42
|
+
protected view: Load<T, ID>
|
|
43
|
+
constructor(viewService: ViewService<T, ID> | Load<T, ID>, keys?: Attributes | Attribute[] | string[]) {
|
|
44
|
+
this.load = this.load.bind(this)
|
|
45
|
+
this.view = getViewFunc(viewService)
|
|
46
|
+
this.keys = getKeysFunc(viewService, keys)
|
|
51
47
|
}
|
|
52
|
-
load(req: Request, res: Response) {
|
|
53
|
-
const id =
|
|
54
|
-
if (
|
|
55
|
-
|
|
48
|
+
load(req: Request, res: Response): void {
|
|
49
|
+
const id = buildAndCheckId<ID>(req, res, this.keys)
|
|
50
|
+
if (id) {
|
|
51
|
+
this.view(id)
|
|
52
|
+
.then((obj) => respondModel(minimize(obj), res))
|
|
53
|
+
.catch((err) => handleError(err, res))
|
|
56
54
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// tslint:disable-next-line:max-classes-per-file
|
|
58
|
+
export class ItemController<T> {
|
|
59
|
+
constructor(
|
|
60
|
+
private loadData: (keyword: string, max?: number) => Promise<T>,
|
|
61
|
+
name?: string,
|
|
62
|
+
protected param?: boolean,
|
|
63
|
+
max?: number,
|
|
64
|
+
maxName?: string,
|
|
65
|
+
) {
|
|
66
|
+
this.name = name && name.length > 0 ? name : "keyword"
|
|
67
|
+
this.max = max && max > 0 ? max : 20
|
|
68
|
+
this.maxName = maxName && maxName.length > 0 ? maxName : "max"
|
|
69
|
+
this.load = this.load.bind(this)
|
|
70
|
+
this.query = this.query.bind(this)
|
|
71
|
+
}
|
|
72
|
+
name: string
|
|
73
|
+
max: number
|
|
74
|
+
maxName: string
|
|
75
|
+
query(req: Request, res: Response) {
|
|
76
|
+
return this.load(req, res)
|
|
77
|
+
}
|
|
78
|
+
load(req: Request, res: Response) {
|
|
79
|
+
const v = this.param ? req.params[this.name] : req.query[this.name]
|
|
80
|
+
if (!v) {
|
|
81
|
+
res.status(400).end(`'${this.name}' cannot be empty`)
|
|
82
|
+
} else {
|
|
83
|
+
const s = v.toString()
|
|
84
|
+
if (s.length === 0) {
|
|
85
|
+
res.status(400).end(`'${this.name}' cannot be empty`)
|
|
60
86
|
} else {
|
|
61
|
-
|
|
87
|
+
const max = queryNumber(req, this.maxName, this.max)
|
|
88
|
+
this.loadData(s, max)
|
|
89
|
+
.then((result) => respondModel(minimize(result), res))
|
|
90
|
+
.catch((err) => handleError(err, res))
|
|
62
91
|
}
|
|
63
|
-
}
|
|
92
|
+
}
|
|
64
93
|
}
|
|
65
94
|
}
|
|
95
|
+
export { ItemController as ItemHandler }
|
|
@@ -1,31 +1,124 @@
|
|
|
1
|
-
import {Request, Response} from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { Request, Response } from "express"
|
|
2
|
+
import { handleError } from "./http"
|
|
3
|
+
import { LoadController, ViewService } from "./LoadController"
|
|
4
|
+
import { Attribute, Attributes } from "./metadata"
|
|
5
|
+
import { resources } from "./resources"
|
|
6
|
+
import { buildArray, Filter, format, fromRequest, getMetadataFunc, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult } from "./search"
|
|
6
7
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
export interface Search {
|
|
9
|
+
search(req: Request, res: Response): void
|
|
10
|
+
load(req: Request, res: Response): void
|
|
11
|
+
}
|
|
12
|
+
export interface Query<T, ID, S> extends ViewService<T, ID> {
|
|
13
|
+
search: (s: S, limit: number, page?: number | string, fields?: string[]) => Promise<SearchResult<T>>
|
|
14
|
+
metadata?(): Attributes | undefined
|
|
15
|
+
load(id: ID, ctx?: any): Promise<T | null>
|
|
16
|
+
}
|
|
17
|
+
export interface SearchManager {
|
|
18
|
+
search(req: Request, res: Response): void
|
|
19
|
+
load(req: Request, res: Response): void
|
|
20
|
+
}
|
|
21
|
+
export function useSearchController<T, ID, S extends Filter>(
|
|
22
|
+
find: (s: S, limit: number, page?: number | string, fields?: string[]) => Promise<SearchResult<T>>,
|
|
23
|
+
viewService: ViewService<T, ID> | ((id: ID, ctx?: any) => Promise<T>),
|
|
24
|
+
array?: string[],
|
|
25
|
+
dates?: string[],
|
|
26
|
+
numbers?: string[],
|
|
27
|
+
keys?: Attributes | Attribute[] | string[],
|
|
28
|
+
config?: SearchConfig | boolean,
|
|
29
|
+
): Search {
|
|
30
|
+
const c = new LoadSearchController(find, viewService, keys, config, dates, numbers)
|
|
31
|
+
c.array = array
|
|
32
|
+
return c
|
|
33
|
+
}
|
|
34
|
+
export const useSearchHandler = useSearchController
|
|
35
|
+
export const createSearchController = useSearchController
|
|
36
|
+
export const createSearchHandler = useSearchController
|
|
37
|
+
export class LoadSearchController<T, ID, S extends Filter> extends LoadController<T, ID> {
|
|
38
|
+
config?: SearchConfig
|
|
39
|
+
csv?: boolean
|
|
40
|
+
dates?: string[]
|
|
41
|
+
numbers?: string[]
|
|
42
|
+
excluding?: string
|
|
43
|
+
array?: string[]
|
|
44
|
+
constructor(
|
|
45
|
+
public find: (s: S, limit: number, page?: number | string, fields?: string[]) => Promise<SearchResult<T>>,
|
|
46
|
+
viewService: ViewService<T, ID> | ((id: ID, ctx?: any) => Promise<T>),
|
|
47
|
+
keys?: Attributes | Attribute[] | string[],
|
|
48
|
+
config?: SearchConfig | boolean,
|
|
49
|
+
dates?: string[],
|
|
50
|
+
numbers?: string[],
|
|
51
|
+
) {
|
|
52
|
+
super(viewService, keys)
|
|
53
|
+
this.search = this.search.bind(this)
|
|
13
54
|
if (config) {
|
|
14
|
-
if (typeof config ===
|
|
15
|
-
this.csv = config
|
|
55
|
+
if (typeof config === "boolean") {
|
|
56
|
+
this.csv = config
|
|
16
57
|
} else {
|
|
17
|
-
this.config = initializeConfig(config)
|
|
58
|
+
this.config = initializeConfig(config)
|
|
18
59
|
if (this.config) {
|
|
19
|
-
this.csv = this.config.csv
|
|
60
|
+
this.csv = this.config.csv
|
|
61
|
+
this.excluding = this.config.excluding
|
|
20
62
|
}
|
|
21
63
|
}
|
|
22
64
|
}
|
|
65
|
+
const m = getMetadataFunc(viewService, dates, numbers, keys)
|
|
66
|
+
if (m) {
|
|
67
|
+
this.dates = m.dates
|
|
68
|
+
this.numbers = m.numbers
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
search(req: Request, res: Response): void {
|
|
72
|
+
const s = fromRequest<S>(req, buildArray(this.array, resources.fields, this.excluding))
|
|
73
|
+
const l = getParameters(s)
|
|
74
|
+
const s2 = format(s, this.dates, this.numbers)
|
|
75
|
+
this.find(s2, l.limit, l.pageOrNextPageToken, l.fields)
|
|
76
|
+
.then((result) => jsonResult(res, result, this.csv, l.fields, this.config))
|
|
77
|
+
.catch((err) => handleError(err, res))
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export class QueryController<T, ID, S extends Filter> extends LoadController<T, ID> {
|
|
81
|
+
config?: SearchConfig
|
|
82
|
+
csv?: boolean
|
|
83
|
+
dates?: string[]
|
|
84
|
+
numbers?: string[]
|
|
85
|
+
excluding?: string
|
|
86
|
+
array?: string[]
|
|
87
|
+
constructor(
|
|
88
|
+
protected query: Query<T, ID, S>,
|
|
89
|
+
config?: SearchConfig | boolean,
|
|
90
|
+
dates?: string[],
|
|
91
|
+
numbers?: string[],
|
|
92
|
+
array?: string[],
|
|
93
|
+
) {
|
|
94
|
+
super(query)
|
|
95
|
+
this.search = this.search.bind(this)
|
|
96
|
+
this.array = array
|
|
97
|
+
if (config) {
|
|
98
|
+
if (typeof config === "boolean") {
|
|
99
|
+
this.csv = config
|
|
100
|
+
} else {
|
|
101
|
+
this.config = initializeConfig(config)
|
|
102
|
+
if (this.config) {
|
|
103
|
+
this.csv = this.config.csv
|
|
104
|
+
this.excluding = this.config.excluding
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const m = getMetadataFunc(query, dates, numbers)
|
|
109
|
+
if (m) {
|
|
110
|
+
this.dates = m.dates
|
|
111
|
+
this.numbers = m.numbers
|
|
112
|
+
}
|
|
23
113
|
}
|
|
24
|
-
search(req: Request, res: Response) {
|
|
25
|
-
const s = fromRequest<S>(req, this.
|
|
26
|
-
const l =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.
|
|
114
|
+
search(req: Request, res: Response): void {
|
|
115
|
+
const s = fromRequest<S>(req, buildArray(this.array, resources.fields, this.excluding))
|
|
116
|
+
const l = getParameters(s)
|
|
117
|
+
const s2 = format(s, this.dates, this.numbers)
|
|
118
|
+
this.query
|
|
119
|
+
.search(s2, l.limit, l.pageOrNextPageToken, l.fields)
|
|
120
|
+
.then((result) => jsonResult(res, result, this.csv, l.fields, this.config))
|
|
121
|
+
.catch((err) => handleError(err, res))
|
|
30
122
|
}
|
|
31
123
|
}
|
|
124
|
+
export { QueryController as QueryHandler }
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Request, Response } from "express"
|
|
2
|
+
import { SimpleMap } from "./log"
|
|
3
|
+
|
|
4
|
+
export interface NumberMap {
|
|
5
|
+
[key: string]: number
|
|
6
|
+
}
|
|
7
|
+
export interface LogConfig {
|
|
8
|
+
level?: string
|
|
9
|
+
map?: LogMapConfig
|
|
10
|
+
constants?: SimpleMap
|
|
11
|
+
name?: Name
|
|
12
|
+
}
|
|
13
|
+
export interface LogMapConfig {
|
|
14
|
+
time?: string
|
|
15
|
+
level?: string
|
|
16
|
+
msg?: string
|
|
17
|
+
}
|
|
18
|
+
export interface LogMap {
|
|
19
|
+
time: string
|
|
20
|
+
level: string
|
|
21
|
+
msg: string
|
|
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
|
+
}
|
|
32
|
+
export interface Logger {
|
|
33
|
+
name: Name
|
|
34
|
+
level: number
|
|
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
|
|
52
|
+
}
|
|
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
|
+
}
|
|
62
|
+
export class LogController {
|
|
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
|
|
68
|
+
this.config = this.config.bind(this)
|
|
69
|
+
}
|
|
70
|
+
config(req: Request, res: Response) {
|
|
71
|
+
const obj: LogConfig = req.body
|
|
72
|
+
if (!this.logger) {
|
|
73
|
+
res.status(503).end("Logger is not available")
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
if (typeof obj.level === "string" && obj.level.length > 0) {
|
|
77
|
+
if (!this.map) {
|
|
78
|
+
res.status(503).end("Map is not available")
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const changed = this.update(this.logger, obj, this.map)
|
|
83
|
+
if (changed) {
|
|
84
|
+
res.status(200).json(true).end()
|
|
85
|
+
} else {
|
|
86
|
+
res.status(204).json(false).end()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function updateLog(logger: Logger, obj: LogConfig, mp: NumberMap): boolean {
|
|
91
|
+
let changed = false
|
|
92
|
+
if (typeof obj.level === "string" && obj.level.length > 0) {
|
|
93
|
+
const lv = mp[obj.level.toUpperCase()]
|
|
94
|
+
if (lv !== undefined) {
|
|
95
|
+
logger.level = lv
|
|
96
|
+
changed = true
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (obj.map) {
|
|
100
|
+
if (typeof obj.map.level === "string" && obj.map.level.length > 0) {
|
|
101
|
+
logger.map.level = obj.map.level
|
|
102
|
+
changed = true
|
|
103
|
+
}
|
|
104
|
+
if (typeof obj.map.time === "string" && obj.map.time.length > 0) {
|
|
105
|
+
logger.map.time = obj.map.time
|
|
106
|
+
changed = true
|
|
107
|
+
}
|
|
108
|
+
if (typeof obj.map.msg === "string" && obj.map.msg.length > 0) {
|
|
109
|
+
logger.map.msg = obj.map.msg
|
|
110
|
+
changed = true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (obj.constants !== undefined && typeof obj.constants === "object") {
|
|
114
|
+
const ks = Object.keys(obj.constants)
|
|
115
|
+
if (ks.length > 0) {
|
|
116
|
+
logger.constants = obj.constants
|
|
117
|
+
} else {
|
|
118
|
+
logger.constants = undefined
|
|
119
|
+
}
|
|
120
|
+
changed = true
|
|
121
|
+
}
|
|
122
|
+
if (obj.name) {
|
|
123
|
+
if (
|
|
124
|
+
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
|
+
) {
|
|
132
|
+
logger.name = obj.name
|
|
133
|
+
changed = true
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return changed
|
|
137
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Request, Response } from "express"
|
|
2
|
+
import { Build, GenericController, GenericService } from "./GenericController"
|
|
3
|
+
import { handleError, Log } from "./http"
|
|
4
|
+
import { ErrorMessage } from "./metadata"
|
|
5
|
+
import { resources, StringMap } from "./resources"
|
|
6
|
+
import { buildArray, Filter, format, fromRequest, getMetadataFunc, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult } from "./search"
|
|
7
|
+
|
|
8
|
+
export interface Service<T, ID, R, S extends Filter> extends GenericService<T, ID, R> {
|
|
9
|
+
search: (s: S, limit: number, page?: number | string, fields?: string[]) => Promise<SearchResult<T>>
|
|
10
|
+
}
|
|
11
|
+
export class LowcodeController<T, ID, S extends Filter> extends GenericController<T, ID> {
|
|
12
|
+
config?: SearchConfig
|
|
13
|
+
csv?: boolean
|
|
14
|
+
dates?: string[]
|
|
15
|
+
numbers?: string[]
|
|
16
|
+
excluding?: string
|
|
17
|
+
array?: string[]
|
|
18
|
+
constructor(
|
|
19
|
+
public lowCodeService: Service<T, ID, number | ErrorMessage[], S>,
|
|
20
|
+
config?: SearchConfig,
|
|
21
|
+
build?: Build<T>,
|
|
22
|
+
validate?: (obj: T, resource?: StringMap, patch?: boolean) => Promise<ErrorMessage[]>,
|
|
23
|
+
dates?: string[],
|
|
24
|
+
numbers?: string[],
|
|
25
|
+
) {
|
|
26
|
+
super(lowCodeService, build, validate)
|
|
27
|
+
this.search = this.search.bind(this)
|
|
28
|
+
this.config = initializeConfig(config)
|
|
29
|
+
if (this.config) {
|
|
30
|
+
this.csv = this.config.csv
|
|
31
|
+
this.excluding = this.config.excluding
|
|
32
|
+
}
|
|
33
|
+
const m = getMetadataFunc(lowCodeService, dates, numbers)
|
|
34
|
+
if (m) {
|
|
35
|
+
this.dates = m.dates
|
|
36
|
+
this.numbers = m.numbers
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
search(req: Request, res: Response) {
|
|
40
|
+
const s = fromRequest<S>(req, buildArray(this.array, resources.fields, this.excluding))
|
|
41
|
+
const l = getParameters(s)
|
|
42
|
+
const s2 = format(s, this.dates, this.numbers)
|
|
43
|
+
this.lowCodeService
|
|
44
|
+
.search(s2, l.limit, l.pageOrNextPageToken, l.fields)
|
|
45
|
+
.then((result) => jsonResult(res, result, this.csv, l.fields, this.config))
|
|
46
|
+
.catch((err) => handleError(err, res))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export { LowcodeController as LowcodeHandler }
|
|
50
|
+
export class Controller<T, ID, S extends Filter> extends GenericController<T, ID> {
|
|
51
|
+
config?: SearchConfig
|
|
52
|
+
csv?: boolean
|
|
53
|
+
dates?: string[]
|
|
54
|
+
numbers?: string[]
|
|
55
|
+
excluding?: string
|
|
56
|
+
array?: string[]
|
|
57
|
+
constructor(
|
|
58
|
+
log: Log,
|
|
59
|
+
public lowCodeService: Service<T, ID, number | T | ErrorMessage[], S>,
|
|
60
|
+
build?: Build<T>,
|
|
61
|
+
validate?: (obj: T, resource?: StringMap, patch?: boolean) => Promise<ErrorMessage[]>,
|
|
62
|
+
config?: SearchConfig,
|
|
63
|
+
dates?: string[],
|
|
64
|
+
numbers?: string[],
|
|
65
|
+
) {
|
|
66
|
+
super(lowCodeService, build, validate)
|
|
67
|
+
this.search = this.search.bind(this)
|
|
68
|
+
this.config = initializeConfig(config)
|
|
69
|
+
if (this.config) {
|
|
70
|
+
this.csv = this.config.csv
|
|
71
|
+
this.excluding = this.config.excluding
|
|
72
|
+
}
|
|
73
|
+
const m = getMetadataFunc(lowCodeService, dates, numbers)
|
|
74
|
+
if (m) {
|
|
75
|
+
this.dates = m.dates
|
|
76
|
+
this.numbers = m.numbers
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
search(req: Request, res: Response) {
|
|
80
|
+
const s = fromRequest<S>(req, buildArray(this.array, resources.fields, this.excluding))
|
|
81
|
+
const l = getParameters(s)
|
|
82
|
+
const s2 = format(s, this.dates, this.numbers)
|
|
83
|
+
this.lowCodeService
|
|
84
|
+
.search(s2, l.limit, l.pageOrNextPageToken, l.fields)
|
|
85
|
+
.then((result) => jsonResult(res, result, this.csv, l.fields, this.config))
|
|
86
|
+
.catch((err) => handleError(err, res))
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/SearchController.ts
CHANGED
|
@@ -1,28 +1,39 @@
|
|
|
1
|
-
import {Request, Response} from
|
|
2
|
-
import {handleError} from
|
|
3
|
-
import {
|
|
1
|
+
import { Request, Response } from "express"
|
|
2
|
+
import { handleError, Log } from "./http"
|
|
3
|
+
import { resources } from "./resources"
|
|
4
|
+
import { buildArray, Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult } from "./search"
|
|
4
5
|
|
|
5
|
-
export class SearchController<T, S extends
|
|
6
|
-
config?: SearchConfig
|
|
7
|
-
csv?: boolean
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
export class SearchController<T, S extends Filter> {
|
|
7
|
+
config?: SearchConfig
|
|
8
|
+
csv?: boolean
|
|
9
|
+
excluding?: string
|
|
10
|
+
array?: string[]
|
|
11
|
+
constructor(
|
|
12
|
+
protected log: Log,
|
|
13
|
+
public find: (s: S, limit: number, page?: number | string, fields?: string[]) => Promise<SearchResult<T>>,
|
|
14
|
+
config?: SearchConfig | boolean,
|
|
15
|
+
public dates?: string[],
|
|
16
|
+
public numbers?: string[],
|
|
17
|
+
) {
|
|
18
|
+
this.search = this.search.bind(this)
|
|
10
19
|
if (config) {
|
|
11
|
-
if (typeof config ===
|
|
12
|
-
this.csv = config
|
|
20
|
+
if (typeof config === "boolean") {
|
|
21
|
+
this.csv = config
|
|
13
22
|
} else {
|
|
14
|
-
this.config = initializeConfig(config)
|
|
23
|
+
this.config = initializeConfig(config)
|
|
15
24
|
if (this.config) {
|
|
16
|
-
this.csv = this.config.csv
|
|
25
|
+
this.csv = this.config.csv
|
|
26
|
+
this.excluding = this.config.excluding
|
|
17
27
|
}
|
|
18
28
|
}
|
|
19
29
|
}
|
|
20
30
|
}
|
|
21
31
|
search(req: Request, res: Response) {
|
|
22
|
-
const s = fromRequest<S>(req, this.
|
|
23
|
-
const l =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.
|
|
32
|
+
const s = fromRequest<S>(req, buildArray(this.array, resources.fields, this.excluding))
|
|
33
|
+
const l = getParameters(s)
|
|
34
|
+
const s2 = format(s, this.dates, this.numbers)
|
|
35
|
+
this.find(s2, l.limit, l.pageOrNextPageToken, l.fields)
|
|
36
|
+
.then((result) => jsonResult(res, result, this.csv, l.fields, this.config))
|
|
37
|
+
.catch((err) => handleError(err, res))
|
|
27
38
|
}
|
|
28
39
|
}
|
package/src/access.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from "express"
|
|
2
|
+
|
|
3
|
+
export interface AccessConfig {
|
|
4
|
+
origin?: string | string[]
|
|
5
|
+
credentials?: string | string[]
|
|
6
|
+
methods?: string | string[]
|
|
7
|
+
headers: number | string | ReadonlyArray<string>
|
|
8
|
+
}
|
|
9
|
+
export type AccessControlAllowConfig = AccessConfig
|
|
10
|
+
export function allow(
|
|
11
|
+
access: AccessConfig,
|
|
12
|
+
): (req: Request, res: Response, next: NextFunction) => void {
|
|
13
|
+
const ao = access.origin
|
|
14
|
+
if (typeof ao === "string") {
|
|
15
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
16
|
+
res.header("Access-Control-Allow-Origin", access.origin)
|
|
17
|
+
res.header("Access-Control-Allow-Credentials", access.credentials)
|
|
18
|
+
res.header("Access-Control-Allow-Methods", access.methods)
|
|
19
|
+
res.setHeader("Access-Control-Allow-Headers", access.headers)
|
|
20
|
+
next()
|
|
21
|
+
}
|
|
22
|
+
} else if (Array.isArray(ao) && ao.length > 0) {
|
|
23
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
24
|
+
const origin = req.headers.origin
|
|
25
|
+
if (origin) {
|
|
26
|
+
if (ao.includes(origin)) {
|
|
27
|
+
res.setHeader("Access-Control-Allow-Origin", origin)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
res.header("Access-Control-Allow-Credentials", access.credentials)
|
|
31
|
+
res.header("Access-Control-Allow-Methods", access.methods)
|
|
32
|
+
res.setHeader("Access-Control-Allow-Headers", access.headers)
|
|
33
|
+
next()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
37
|
+
res.header("Access-Control-Allow-Credentials", access.credentials)
|
|
38
|
+
res.header("Access-Control-Allow-Methods", access.methods)
|
|
39
|
+
res.setHeader("Access-Control-Allow-Headers", access.headers)
|
|
40
|
+
next()
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as http from "http"
|
|
2
|
+
import * as https from "https"
|
|
3
|
+
import { AnyMap, HealthChecker } from "./health"
|
|
4
|
+
|
|
5
|
+
function getHealthSecure(url: string, timeout: number): Promise<AnyMap> {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
https
|
|
8
|
+
.get(url, { rejectUnauthorized: false }, (res: any) => {
|
|
9
|
+
let data = ""
|
|
10
|
+
res.on("data", (d: any) => {
|
|
11
|
+
data += d
|
|
12
|
+
})
|
|
13
|
+
res.on("end", () => {
|
|
14
|
+
resolve({ statusCode: res.statusCode, data, statusMessage: res.statusMessage })
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
.on("error", (e: any) => {
|
|
18
|
+
return { statusCode: 500, statusMessage: e }
|
|
19
|
+
})
|
|
20
|
+
setTimeout(() => resolve({ statusCode: 408, statusMessage: "Time out" }), timeout)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
function getHealth(url: string, timeout: number): Promise<AnyMap> {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
http
|
|
26
|
+
.get(url, (res: any) => {
|
|
27
|
+
let data = ""
|
|
28
|
+
res.on("data", (d: any) => {
|
|
29
|
+
data += d
|
|
30
|
+
})
|
|
31
|
+
res.on("end", () => {
|
|
32
|
+
resolve({ statusCode: res.statusCode, data, statusMessage: res.statusMessage })
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
.on("error", (e: any) => {
|
|
36
|
+
return { statusCode: 500, statusMessage: e }
|
|
37
|
+
})
|
|
38
|
+
setTimeout(() => resolve({ statusCode: 408, statusMessage: "Time out" }), timeout)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
export class ClientChecker implements HealthChecker {
|
|
42
|
+
timeout: number
|
|
43
|
+
constructor(private service: string, private url: string, timeout: number) {
|
|
44
|
+
this.timeout = timeout ? timeout : 4200
|
|
45
|
+
this.check = this.check.bind(this)
|
|
46
|
+
this.name = this.name.bind(this)
|
|
47
|
+
this.build = this.build.bind(this)
|
|
48
|
+
}
|
|
49
|
+
check(): Promise<AnyMap> {
|
|
50
|
+
let obj = {} as AnyMap
|
|
51
|
+
if (this.url.startsWith("https://")) {
|
|
52
|
+
return getHealthSecure(this.url, this.timeout).then((r) => (obj = r))
|
|
53
|
+
} else {
|
|
54
|
+
return getHealth(this.url, this.timeout).then((r) => (obj = r))
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
name(): string {
|
|
58
|
+
return this.service
|
|
59
|
+
}
|
|
60
|
+
build(data: AnyMap, err: any): AnyMap {
|
|
61
|
+
if (err) {
|
|
62
|
+
if (!data) {
|
|
63
|
+
data = {} as AnyMap
|
|
64
|
+
}
|
|
65
|
+
data["error"] = err
|
|
66
|
+
}
|
|
67
|
+
return data
|
|
68
|
+
}
|
|
69
|
+
}
|