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/lib/view.js CHANGED
@@ -1,63 +1,77 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ "use strict"
2
+ Object.defineProperty(exports, "__esModule", { value: true })
3
+ function buildAndCheckId(req, res, keys) {
4
+ var id = buildId(req, keys)
5
+ if (!id) {
6
+ res.status(400).end("invalid parameters")
7
+ return undefined
8
+ }
9
+ return id
10
+ }
11
+ exports.buildAndCheckId = buildAndCheckId
3
12
  function buildId(req, attrs) {
4
- if (!attrs) {
5
- var id = req.params['id'];
13
+ if (!attrs || attrs.length === 0) {
14
+ var id = req.params["id"]
6
15
  if (id && id.length > 0) {
7
- return id;
16
+ return id
8
17
  }
9
- return null;
18
+ return undefined
10
19
  }
11
20
  if (attrs && attrs.length === 1) {
12
- var key = (attrs[0].name ? attrs[0].name : 'id');
13
- var id = req.params[key];
21
+ var id = req.params["id"]
22
+ var n = attrs[0].name
23
+ if ((!id || id.length === 0) && n && n.length > 0) {
24
+ id = req.params[n]
25
+ }
14
26
  if (id && id.length > 0) {
15
- if (attrs[0].type === 'integer' || attrs[0].type === 'number') {
27
+ if (attrs[0].type === "integer" || attrs[0].type === "number") {
16
28
  if (isNaN(id)) {
17
- return null;
29
+ return undefined
18
30
  }
19
- var v = parseFloat(id);
20
- return v;
31
+ var v = parseFloat(id)
32
+ return v
21
33
  }
22
- return id;
34
+ return id
23
35
  }
24
36
  }
25
- var ids = {};
37
+ var ids = {}
26
38
  for (var _i = 0, attrs_1 = attrs; _i < attrs_1.length; _i++) {
27
- var attr = attrs_1[_i];
28
- var v = req.params[attr.name];
39
+ var attr = attrs_1[_i]
40
+ if (!attr.name) {
41
+ return undefined
42
+ }
43
+ var v = req.params[attr.name]
29
44
  if (!v) {
30
- return null;
45
+ return undefined
31
46
  }
32
- if (attr.type === 'integer' || attr.type === 'number') {
47
+ if (attr.type === "integer" || attr.type === "number") {
33
48
  if (isNaN(v)) {
34
- return null;
49
+ return undefined
35
50
  }
36
- ids[attr.name] = parseFloat(v);
37
- }
38
- else {
39
- ids[attr.name] = v;
51
+ ids[attr.name] = parseFloat(v)
52
+ } else {
53
+ ids[attr.name] = v
40
54
  }
41
- return ids;
55
+ return ids
42
56
  }
43
57
  }
44
- exports.buildId = buildId;
58
+ exports.buildId = buildId
45
59
  function buildKeys(attrs) {
46
60
  if (!attrs) {
47
- return undefined;
61
+ return undefined
48
62
  }
49
- var keys = Object.keys(attrs);
50
- var ats = [];
63
+ var keys = Object.keys(attrs)
64
+ var ats = []
51
65
  for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
52
- var key = keys_1[_i];
53
- var attr = attrs[key];
66
+ var key = keys_1[_i]
67
+ var attr = attrs[key]
54
68
  if (attr) {
55
69
  if (attr.key === true) {
56
- var at = { name: key, type: attr.type };
57
- ats.push(at);
70
+ var at = { name: key, type: attr.type }
71
+ ats.push(at)
58
72
  }
59
73
  }
60
74
  }
61
- return ats;
75
+ return ats
62
76
  }
63
- exports.buildKeys = buildKeys;
77
+ exports.buildKeys = buildKeys
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-express",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "core-express",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./src/index.ts",
@@ -21,11 +21,15 @@
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "git@github.com/common-ts/express"
24
+ "url": "git@github.com/core-ts/core-express"
25
25
  },
26
26
  "keywords": [
27
27
  "express",
28
- "core-express",
29
- "core express"
28
+ "express-plus",
29
+ "express plus",
30
+ "express-ext",
31
+ "express ext",
32
+ "express-extension",
33
+ "express extension"
30
34
  ]
31
35
  }
@@ -1,134 +1,245 @@
1
- import {Request, Response} from 'express';
2
- import {checkId, create, initializeStatus, isTypeError, ResultInfo, StatusConfig, update} from './edit';
3
- import {LoadController} from './LoadController';
4
- import {Attributes, ErrorMessage, Model} from './metadata';
5
- import {resources} from './resources';
6
- import {handleError} from './response';
7
- import {buildId} from './view';
1
+ import { Request, Response } from "express"
2
+ import { checkId, create, getStatusCode, update } from "./edit"
3
+ import { handleError } from "./http"
4
+ import { LoadController } from "./LoadController"
5
+ import { Attribute, Attributes, ErrorMessage } from "./metadata"
6
+ import { resources, StringMap } from "./resources"
7
+ import { buildAndCheckId, buildId } from "./view"
8
8
 
9
+ export type Build<T> = (res: Response, obj: T, isCreate?: boolean, isPatch?: boolean) => void
10
+ export type Validate<T> = (obj: T, resource?: StringMap, patch?: boolean) => Promise<ErrorMessage[]>
11
+ export type Save<T> = (obj: T, ctx?: any) => Promise<number | T | ErrorMessage[]>
9
12
  export interface GenericService<T, ID, R> {
10
- metadata?(): Model;
11
- load(id: ID, ctx?: any): Promise<T>;
12
- insert(obj: T, ctx?: any): Promise<R>;
13
- update(obj: T, ctx?: any): Promise<R>;
14
- patch?(obj: T, ctx?: any): Promise<R>;
15
- delete?(id: ID, ctx?: any): Promise<number>;
13
+ metadata?(): Attributes | undefined
14
+ load(id: ID, ctx?: any): Promise<T | null>
15
+ create(obj: T, ctx?: any): Promise<R>
16
+ update(obj: T, ctx?: any): Promise<R>
17
+ patch?(obj: Partial<T>, ctx?: any): Promise<R>
18
+ delete?(id: ID, ctx?: any): Promise<number>
16
19
  }
17
20
  export class GenericController<T, ID> extends LoadController<T, ID> {
18
- status: StatusConfig;
19
- metadata: Attributes;
20
- constructor(log: (msg: string, ctx?: any) => void, public service: GenericService<T, ID, number|ResultInfo<T>>, status?: StatusConfig, public validate?: (obj: T, patch?: boolean) => Promise<ErrorMessage[]>) {
21
- super(log, service);
22
- this.status = initializeStatus(status);
21
+ metadata?: Attributes
22
+ returnNumber?: boolean
23
+ constructor(
24
+ public service: GenericService<T, ID, number | T | ErrorMessage[]>,
25
+ public build?: Build<T>,
26
+ public validate?: Validate<T>,
27
+ returnNumber?: boolean,
28
+ ) {
29
+ super(service)
30
+ this.returnNumber = returnNumber
23
31
  if (service.metadata) {
24
- const m = service.metadata();
32
+ const m = service.metadata()
25
33
  if (m) {
26
- this.metadata = m.attributes;
34
+ this.metadata = m
27
35
  }
28
36
  }
29
- this.insert = this.insert.bind(this);
30
- this.update = this.update.bind(this);
31
- this.patch = this.patch.bind(this);
32
- this.delete = this.delete.bind(this);
37
+ this.create = this.create.bind(this)
38
+ this.update = this.update.bind(this)
39
+ this.patch = this.patch.bind(this)
40
+ this.delete = this.delete.bind(this)
33
41
  if (!validate && resources.createValidator && this.metadata) {
34
- const v = resources.createValidator(this.metadata);
35
- this.validate = v.validate;
42
+ const v = resources.createValidator(this.metadata)
43
+ this.validate = v.validate
36
44
  }
37
45
  }
38
- insert(req: Request, res: Response) {
39
- const obj = req.body;
40
- if (!obj) {
41
- return res.status(400).end('The request body cannot be empty.');
42
- }
43
- if (this.validate) {
44
- const l = this.log;
45
- this.validate(obj).then(errors => {
46
- if (errors && errors.length > 0) {
47
- const r: ResultInfo<T> = {status: this.status.validation_error, errors};
48
- res.status(getStatusCode(errors)).json(r);
49
- } else {
50
- create(res, this.status, obj, this.service.insert, this.log);
51
- }
52
- }).catch(err => handleError(err, res, l));
53
- } else {
54
- create(res, this.status, obj, this.service.insert, this.log);
46
+ create(req: Request, res: Response): void {
47
+ validateAndCreate(req, res, this.service.create, this.validate, this.build)
48
+ }
49
+ update(req: Request, res: Response): void {
50
+ const id = buildAndCheckIdWithBody<T, ID, any>(req, res, this.keys, this.service.update)
51
+ if (id) {
52
+ validateAndUpdate(res, req.body, false, this.service.update, this.validate, undefined, this.build)
55
53
  }
56
54
  }
57
- update(req: Request, res: Response) {
58
- const obj = req.body;
59
- if (!obj) {
60
- return res.status(400).end('The request body cannot be empty.');
55
+ patch(req: Request, res: Response): void {
56
+ const id = buildAndCheckIdWithBody<T, ID, any>(req, res, this.keys, this.service.patch)
57
+ if (id && this.service.patch) {
58
+ validateAndUpdate(res, req.body, true, this.service.patch, this.validate, undefined, this.build)
61
59
  }
62
- const id = buildId<ID>(req, this.keys);
63
- if (!id) {
64
- return res.status(400).end('Invalid parameters');
60
+ }
61
+ delete(req: Request, res: Response): void {
62
+ const id = buildAndCheckId<ID>(req, res, this.keys)
63
+ if (id) {
64
+ if (!this.service.delete) {
65
+ res.status(405).end("Method Not Allowed")
66
+ } else {
67
+ this.service
68
+ .delete(id)
69
+ .then((count) => {
70
+ res.status(getDeleteStatus(count)).json(count).end()
71
+ })
72
+ .catch((err) => handleError(err, res))
73
+ }
65
74
  }
66
- const ok = checkId<T, ID>(obj, id, this.keys);
67
- if (!ok) {
68
- return res.status(400).end('Invalid parameters');
75
+ }
76
+ }
77
+ export function validateAndCreate<T>(req: Request, res: Response, save: Save<T>, validate?: Validate<T>, build?: Build<T>, returnNumber?: boolean): void {
78
+ const obj = req.body
79
+ if (!obj || obj === "") {
80
+ res.status(400).end("The request body cannot be empty.")
81
+ } else {
82
+ if (validate) {
83
+ validate(obj)
84
+ .then((errors) => {
85
+ if (errors && errors.length > 0) {
86
+ res.status(getStatusCode(errors)).json(errors).end()
87
+ } else {
88
+ if (build) {
89
+ build(res, obj, true)
90
+ }
91
+ create(res, obj, save, returnNumber)
92
+ }
93
+ })
94
+ .catch((err) => handleError(err, res))
95
+ } else {
96
+ create(res, obj, save, returnNumber)
69
97
  }
70
- if (this.validate) {
71
- const l = this.log;
72
- this.validate(obj).then(errors => {
98
+ }
99
+ }
100
+ export function validateAndUpdate<T>(
101
+ res: Response,
102
+ obj: T,
103
+ isPatch: boolean,
104
+ save: Save<T>,
105
+ validate?: Validate<T>,
106
+ resource?: StringMap,
107
+ build?: Build<T>,
108
+ returnNumber?: boolean,
109
+ ): void {
110
+ if (validate) {
111
+ validate(obj, resource, isPatch)
112
+ .then((errors) => {
73
113
  if (errors && errors.length > 0) {
74
- const r: ResultInfo<T> = {status: this.status.validation_error, errors};
75
- res.status(getStatusCode(errors)).json(r);
114
+ res.status(getStatusCode(errors)).json(errors).end()
76
115
  } else {
77
- update(res, this.status, obj, this.service.update, this.log);
116
+ if (build) {
117
+ build(res, obj, false, isPatch)
118
+ }
119
+ update(res, obj, save, returnNumber)
78
120
  }
79
- }).catch(err => handleError(err, res, l));
80
- } else {
81
- update(res, this.status, obj, this.service.update, this.log);
82
- }
121
+ })
122
+ .catch((err) => handleError(err, res))
123
+ } else {
124
+ update(res, obj, save, returnNumber)
83
125
  }
84
- patch(req: Request, res: Response) {
85
- const obj = req.body;
86
- if (!obj) {
87
- return res.status(400).end('The request body cannot be empty.');
88
- }
89
- const id = buildId<ID>(req, this.keys);
90
- if (!id) {
91
- return res.status(400).end('Invalid parameters');
126
+ }
127
+ export function buildAndCheckIdWithBody<T, ID, R>(req: Request, res: Response, keys?: Attribute[], patch?: (obj: T, ctx?: any) => Promise<R>): ID | undefined {
128
+ const obj = req.body
129
+ if (!obj || obj === "") {
130
+ res.status(400).end("The request body cannot be empty.")
131
+ return undefined
132
+ }
133
+ if (!patch) {
134
+ res.status(405).end("Method Not Allowed")
135
+ return undefined
136
+ }
137
+ const id = buildId<ID>(req, keys)
138
+ if (!id) {
139
+ res.status(400).end("Invalid parameters")
140
+ return undefined
141
+ }
142
+ const ok = checkId<T, ID>(obj, id, keys)
143
+ if (!ok) {
144
+ res.status(400).end("body and url are not matched")
145
+ return undefined
146
+ }
147
+ return id
148
+ }
149
+ export function getDeleteStatus(count: number): number {
150
+ if (count > 0) {
151
+ return 200
152
+ } else if (count === 0) {
153
+ return 404
154
+ } else {
155
+ return 409
156
+ }
157
+ }
158
+
159
+ export interface ModelConfig {
160
+ id?: string
161
+ payload?: string
162
+ user?: string
163
+ updatedBy?: string
164
+ updatedAt?: string
165
+ createdBy?: string
166
+ createdAt?: string
167
+ version?: string
168
+ }
169
+ export function useBuild<T>(c: ModelConfig, generate?: () => string): Build<T> {
170
+ const b = new Builder<T>(
171
+ generate,
172
+ c.id ? c.id : "",
173
+ c.payload ? c.payload : "",
174
+ c.user ? c.user : "",
175
+ c.updatedBy ? c.updatedBy : "",
176
+ c.updatedAt ? c.updatedAt : "",
177
+ c.createdBy ? c.createdBy : "",
178
+ c.createdAt ? c.createdAt : "",
179
+ c.version ? c.version : "",
180
+ )
181
+ return b.build
182
+ }
183
+ // tslint:disable-next-line:max-classes-per-file
184
+ export class Builder<T> {
185
+ constructor(
186
+ public generate: (() => string) | undefined,
187
+ public id: string,
188
+ public payload: string,
189
+ public user: string,
190
+ public updatedBy: string,
191
+ public updatedAt: string,
192
+ public createdBy: string,
193
+ public createdAt: string,
194
+ public version: string,
195
+ ) {
196
+ this.build = this.build.bind(this)
197
+ }
198
+ build(res: Response, obj: T, isCreate?: boolean, isPatch?: boolean): void {
199
+ const o: any = obj
200
+ let usr = ""
201
+ if (this.user.length > 0) {
202
+ if (this.payload.length > 0) {
203
+ const payload = res.locals[this.payload]
204
+ if (payload) {
205
+ usr = payload[this.user]
206
+ }
207
+ } else {
208
+ usr = res.locals[this.user]
209
+ }
92
210
  }
93
- const ok = checkId<T, ID>(obj, id, this.keys);
94
- if (!ok) {
95
- return res.status(400).end('Invalid parameters');
211
+ if (!usr) {
212
+ usr = ""
96
213
  }
97
- if (this.validate) {
98
- const l = this.log;
99
- this.validate(obj, true).then(errors => {
100
- if (errors && errors.length > 0) {
101
- const r: ResultInfo<T> = {status: this.status.validation_error, errors};
102
- res.status(getStatusCode(errors)).json(r);
103
- } else {
104
- update(res, this.status, obj, this.service.patch, this.log);
214
+ const now = new Date()
215
+ if (isCreate) {
216
+ if (this.generate && this.id.length > 0) {
217
+ o[this.id] = this.generate()
218
+ }
219
+ if (usr.length > 0) {
220
+ if (this.createdAt.length > 0) {
221
+ o[this.createdAt] = now
105
222
  }
106
- }).catch(err => handleError(err, res, l));
107
- } else {
108
- update(res, this.status, obj, this.service.update, this.log);
109
- }
110
- }
111
- delete(req: Request, res: Response) {
112
- const id = buildId<ID>(req, this.keys);
113
- if (!id) {
114
- return res.status(400).end('invalid parameters');
223
+ if (this.createdBy.length > 0) {
224
+ o[this.createdBy] = usr
225
+ }
226
+ }
227
+ if (this.version.length > 0) {
228
+ o[this.version] = 1
229
+ }
230
+ } else if (isPatch) {
231
+ const keys = Object.keys(o)
232
+ if (keys.length === 0) {
233
+ return
234
+ }
115
235
  }
116
- this.service.delete(id).then(count => {
117
- if (count > 0) {
118
- res.status(200).json(count);
119
- } else if (count === 0) {
120
- res.status(404).json(count);
121
- } else {
122
- res.status(409).json(count);
236
+ if (usr.length > 0) {
237
+ if (this.updatedAt.length > 0) {
238
+ o[this.updatedAt] = now
123
239
  }
124
- }).catch(err => {
125
- if (this.log) {
126
- this.log(err as any);
240
+ if (this.updatedBy.length > 0) {
241
+ o[this.updatedBy] = usr
127
242
  }
128
- res.status(500).end('Internal Server Error');
129
- });
243
+ }
130
244
  }
131
245
  }
132
- function getStatusCode(errs: ErrorMessage[]): number {
133
- return (isTypeError(errs) ? 400 : 422);
134
- }
@@ -1,28 +1,45 @@
1
- import {Request, Response} from 'express';
2
- import {ResultInfo, StatusConfig} from './edit';
3
- import {GenericController, GenericService} from './GenericController';
4
- import {ErrorMessage} from './metadata';
5
- import {handleError} from './response';
6
- import {fromRequest, getLimit, initializeConfig, jsonResult, SearchConfig, SearchModel, SearchResult} from './search';
1
+ import { Request, Response } from "express"
2
+ import { Build, GenericController, GenericService } from "./GenericController"
3
+ import { handleError } 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
7
 
8
- export interface Config extends StatusConfig, SearchConfig {
9
- }
10
- export class GenericSearchController<T, ID, S extends SearchModel> extends GenericController<T, ID> {
11
- config?: SearchConfig;
12
- csv?: boolean;
13
- constructor(log: (msg: string, ctx?: any) => void, public find: (s: S, limit?: number, skip?: number, refId?: string) => Promise<SearchResult<T>>, service: GenericService<T, ID, number|ResultInfo<T>>, config?: Config, validate?: (obj: T, patch?: boolean) => Promise<ErrorMessage[]>, public format?: (s: S) => S) {
14
- super(log, service, config, validate);
15
- this.search = this.search.bind(this);
16
- this.config = initializeConfig(config);
8
+ export class GenericSearchController<T, ID, S extends Filter> extends GenericController<T, ID> {
9
+ config?: SearchConfig
10
+ csv?: boolean
11
+ dates?: string[]
12
+ numbers?: string[]
13
+ excluding?: string
14
+ array?: string[]
15
+ constructor(
16
+ public find: (s: S, limit: number, page?: number | string, fields?: string[]) => Promise<SearchResult<T>>,
17
+ service: GenericService<T, ID, number | ErrorMessage[]>,
18
+ config?: SearchConfig,
19
+ build?: Build<T>,
20
+ validate?: (obj: T, resource?: StringMap, patch?: boolean) => Promise<ErrorMessage[]>,
21
+ dates?: string[],
22
+ numbers?: string[],
23
+ ) {
24
+ super(service, build, validate)
25
+ this.search = this.search.bind(this)
26
+ this.config = initializeConfig(config)
17
27
  if (this.config) {
18
- this.csv = this.config.csv;
28
+ this.csv = this.config.csv
29
+ this.excluding = this.config.excluding
30
+ }
31
+ const m = getMetadataFunc(service, dates, numbers)
32
+ if (m) {
33
+ this.dates = m.dates
34
+ this.numbers = m.numbers
19
35
  }
20
36
  }
21
37
  search(req: Request, res: Response) {
22
- const s = fromRequest<S>(req, this.format);
23
- const l = getLimit(s);
24
- this.find(s, l.limit, l.skip, l.refId)
25
- .then(result => jsonResult(res, result, this.csv, s.fields, this.config))
26
- .catch(err => handleError(err, res, this.log));
38
+ const s = fromRequest<S>(req, buildArray(this.array, resources.fields, this.excluding))
39
+ const l = getParameters(s)
40
+ const s2 = format(s, this.dates, this.numbers)
41
+ this.find(s2, l.limit, l.pageOrNextPageToken, l.fields)
42
+ .then((result) => jsonResult(res, result, this.csv, l.fields, this.config))
43
+ .catch((err) => handleError(err, res))
27
44
  }
28
45
  }
@@ -1,17 +1,17 @@
1
- import {Request, Response} from 'express';
2
- import {check, HealthChecker} from './health';
1
+ import { Request, Response } from "express"
2
+ import { health, HealthChecker } from "./health"
3
3
 
4
4
  export class HealthController {
5
5
  constructor(protected checkers: HealthChecker[]) {
6
- this.check = this.check.bind(this);
6
+ this.check = this.check.bind(this)
7
7
  }
8
- async check(req: Request, res: Response) {
9
- check(this.checkers).then(heath => {
10
- if (heath.status === 'UP') {
11
- return res.status(200).json(heath);
8
+ check(req: Request, res: Response) {
9
+ health(this.checkers).then((r) => {
10
+ if (r.status === "UP") {
11
+ res.status(200).json(r).end()
12
12
  } else {
13
- return res.status(500).json(heath);
13
+ res.status(500).json(r).end()
14
14
  }
15
- });
15
+ })
16
16
  }
17
17
  }