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/src/search.ts CHANGED
@@ -1,270 +1,854 @@
1
- import {Request, Response} from 'express';
1
+ import { Request, Response } from "express"
2
+ import { minimizeArray, query, queryNumber } from "./http"
3
+ import { ViewService } from "./LoadController"
4
+ import { Attribute, Attributes } from "./metadata"
5
+ import { resources, StringMap } from "./resources"
2
6
 
3
- export interface ArrayMap {
4
- [key: string]: string[]|number[];
5
- }
7
+ const et = ""
8
+
9
+ export interface Filter {
10
+ page?: number
11
+ limit?: number
6
12
 
7
- export interface SearchModel {
8
- fields?: string[];
9
- sort?: string;
13
+ fields?: string[]
14
+ sort?: string
10
15
 
11
- keyword?: string;
12
- excluding?: ArrayMap;
16
+ q?: string
13
17
  }
14
18
  export interface SearchConfig {
15
- list?: string;
16
- total?: string;
17
- token?: string;
18
- last?: string;
19
- csv?: boolean;
19
+ excluding?: string
20
+ // fields?: string
21
+ list?: string
22
+ total?: string
23
+ token?: string
24
+ // last?: string
25
+ csv?: boolean
26
+ // page?: string
27
+ // limit?: string
28
+ // skip?: string
29
+ // refId?: string;
30
+ // firstLimit?: string
20
31
  }
21
32
  export interface SearchResult<T> {
22
- list: T[];
23
- total?: number;
24
- last?: boolean;
33
+ list: T[]
34
+ total?: number
35
+ nextPageToken?: string
36
+ }
37
+
38
+ export function getPage<F extends Filter>(req: Request, filter?: F): number {
39
+ if (filter) {
40
+ const v0 = (filter as any)[resources.page]
41
+ if (v0 == undefined) {
42
+ const field = req.query[resources.page]
43
+ const v = field ? field.toString() : undefined
44
+ if (!v || v.length === 0) {
45
+ ;(filter as any)[resources.page] = 1
46
+ return 1
47
+ }
48
+ if (isNaN(v as any)) {
49
+ ;(filter as any)[resources.page] = 1
50
+ return 1
51
+ } else {
52
+ let n = parseFloat(v)
53
+ if (n < 1) {
54
+ n = 1
55
+ }
56
+ ;(filter as any)[resources.page] = n
57
+ return n
58
+ }
59
+ } else if (typeof v0 === "number") {
60
+ if (v0 > 0) {
61
+ return v0
62
+ } else {
63
+ ;(filter as any)[resources.page] = 1
64
+ return 1
65
+ }
66
+ }
67
+ }
68
+ const field = req.query[resources.page]
69
+ const v = field ? field.toString() : undefined
70
+ if (!v || v.length === 0) {
71
+ if (filter) {
72
+ ;(filter as any)[resources.page] = 1
73
+ }
74
+ return 1
75
+ }
76
+ if (isNaN(v as any)) {
77
+ if (filter) {
78
+ ;(filter as any)[resources.page] = 1
79
+ }
80
+ return 1
81
+ }
82
+ let n = parseFloat(v)
83
+ if (n < 1) {
84
+ n = 1
85
+ }
86
+ if (filter) {
87
+ ;(filter as any)[resources.page] = n
88
+ }
89
+ return n
90
+ }
91
+
92
+ export function getLimit<F extends Filter>(req: Request, filter?: F): number {
93
+ if (filter) {
94
+ const v0 = (filter as any)[resources.limit]
95
+ if (v0 == undefined) {
96
+ const field = req.query[resources.limit]
97
+ const v = field ? field.toString() : undefined
98
+ if (!v || v.length === 0) {
99
+ ;(filter as any)[resources.limit] = resources.defaultLimit
100
+ return resources.defaultLimit
101
+ }
102
+ if (isNaN(v as any)) {
103
+ ;(filter as any)[resources.limit] = resources.defaultLimit
104
+ return 1
105
+ } else {
106
+ let n = parseFloat(v)
107
+ if (n < 1) {
108
+ n = resources.defaultLimit
109
+ }
110
+ ;(filter as any)[resources.limit] = n
111
+ return n
112
+ }
113
+ } else if (typeof v0 === "number") {
114
+ if (v0 > 0) {
115
+ return v0
116
+ } else {
117
+ ;(filter as any)[resources.limit] = resources.defaultLimit
118
+ return resources.defaultLimit
119
+ }
120
+ }
121
+ }
122
+ const field = req.query[resources.limit]
123
+ const v = field ? field.toString() : undefined
124
+ if (!v || v.length === 0) {
125
+ if (filter) {
126
+ ;(filter as any)[resources.limit] = resources.defaultLimit
127
+ }
128
+ return resources.defaultLimit
129
+ }
130
+ if (isNaN(v as any)) {
131
+ if (filter) {
132
+ ;(filter as any)[resources.limit] = resources.defaultLimit
133
+ }
134
+ return resources.defaultLimit
135
+ }
136
+ let n = parseFloat(v)
137
+ if (n < 1) {
138
+ n = resources.defaultLimit
139
+ }
140
+ if (filter) {
141
+ ;(filter as any)[resources.limit] = n
142
+ }
143
+ return n
144
+ }
145
+ export function queryLimit(req: Request): number {
146
+ return queryNumber(req, resources.limit, resources.defaultLimit)
147
+ }
148
+ export function queryPage<F extends Filter>(req: Request, filter?: F): number {
149
+ const field = req.query[resources.page]
150
+ const v = field ? field.toString() : undefined
151
+ if (!v || v.length === 0) {
152
+ ;(filter as any)[resources.page] = 1
153
+ return 1
154
+ }
155
+ if (isNaN(v as any)) {
156
+ ;(filter as any)[resources.page] = 1
157
+ return 1
158
+ }
159
+ const n = parseFloat(v)
160
+ ;(filter as any)[resources.page] = n
161
+ return n
162
+ }
163
+ export function getOffset(limit: number, page?: number): number {
164
+ return !page || page <= 1 || limit <= 0 ? 0 : limit * (page - 1)
165
+ }
166
+
167
+ export function getPageTotal(pageSize?: number, total?: number): number {
168
+ if (!pageSize || pageSize <= 0) {
169
+ return 1
170
+ } else {
171
+ if (!total) {
172
+ total = 0
173
+ }
174
+ if (total % pageSize === 0) {
175
+ return Math.floor(total / pageSize)
176
+ }
177
+ return Math.floor(total / pageSize + 1)
178
+ }
179
+ }
180
+ export function formatText(...args: any[]): string {
181
+ let formatted = args[0]
182
+ if (!formatted || formatted === "") {
183
+ return ""
184
+ }
185
+ if (args.length > 1 && Array.isArray(args[1])) {
186
+ const params = args[1]
187
+ for (let i = 0; i < params.length; i++) {
188
+ const regexp = new RegExp("\\{" + i + "\\}", "gi")
189
+ formatted = formatted.replace(regexp, params[i])
190
+ }
191
+ } else {
192
+ for (let i = 1; i < args.length; i++) {
193
+ const regexp = new RegExp("\\{" + (i - 1) + "\\}", "gi")
194
+ formatted = formatted.replace(regexp, args[i])
195
+ }
196
+ }
197
+ return formatted
198
+ }
199
+ export function buildMessage<T>(resource: StringMap, results: T[], limit: number, page: number | undefined, total?: number): string {
200
+ if (!results || results.length === 0) {
201
+ return resource.msg_no_data_found
202
+ } else {
203
+ if (!page) {
204
+ page = 1
205
+ }
206
+ const fromIndex = (page - 1) * limit + 1
207
+ const toIndex = fromIndex + results.length - 1
208
+ const pageTotal = getPageTotal(limit, total)
209
+ if (pageTotal > 1) {
210
+ const msg2 = formatText(resource.msg_search_result_page_sequence, fromIndex, toIndex, total, page, pageTotal)
211
+ return msg2
212
+ } else {
213
+ const msg3 = formatText(resource.msg_search_result_sequence, fromIndex, toIndex)
214
+ return msg3
215
+ }
216
+ }
217
+ }
218
+ export function buildPages(pageSize?: number, total?: number): number[] {
219
+ const pageTotal = getPageTotal(pageSize, total)
220
+ if (pageTotal <= 1) {
221
+ return [1]
222
+ }
223
+ const arr: number[] = []
224
+ for (let i = 1; i <= pageTotal; i++) {
225
+ arr.push(i)
226
+ }
227
+ return arr
25
228
  }
26
229
 
230
+ export function hasSearch(req: Request): boolean {
231
+ return req.url.indexOf("?") >= 0
232
+ }
233
+ export function getSearch(url: string): string {
234
+ const i = url.indexOf("?")
235
+ return i < 0 ? et : url.substring(i + 1)
236
+ }
237
+ export function getField(search: string, fieldName: string): string {
238
+ let i = search.indexOf(fieldName + "=")
239
+ if (i < 0) {
240
+ return ""
241
+ }
242
+ if (i > 0) {
243
+ if (search.substring(i - 1, 1) != "&") {
244
+ i = search.indexOf("&" + fieldName + "=")
245
+ if (i < 0) {
246
+ return search
247
+ }
248
+ i = i + 1
249
+ }
250
+ }
251
+ const j = search.indexOf("&", i + fieldName.length)
252
+ return j >= 0 ? search.substring(i, j) : search.substring(i)
253
+ }
254
+ export function removeField(search: string, fieldName: string): string {
255
+ let i = search.indexOf(fieldName + "=")
256
+ if (i < 0) {
257
+ return search
258
+ }
259
+ if (i > 0) {
260
+ if (search.substring(i - 1, 1) != "&") {
261
+ i = search.indexOf("&" + fieldName + "=")
262
+ if (i < 0) {
263
+ return search
264
+ }
265
+ i = i + 1
266
+ }
267
+ }
268
+ const j = search.indexOf("&", i + fieldName.length)
269
+ return j >= 0 ? search.substring(0, i) + search.substring(j + 1) : search.substring(0, i - 1)
270
+ }
271
+ export function removePage(search: string): string {
272
+ search = removeField(search, resources.page)
273
+ search = removeField(search, resources.partial)
274
+ return search
275
+ }
276
+ export function buildPageSearch(search: string): string {
277
+ const sr = removePage(search)
278
+ return sr.length == 0 ? sr : "&" + sr
279
+ }
280
+ export function buildPageSearchFromUrl(url: string): string {
281
+ const search = getSearch(url)
282
+ return buildPageSearch(search)
283
+ }
284
+ export function removeSort(search: string): string {
285
+ search = removeField(search, resources.sort)
286
+ search = removeField(search, resources.partial)
287
+ return search
288
+ }
289
+ export interface Sort {
290
+ field?: string
291
+ type?: string
292
+ }
293
+ export interface SortType {
294
+ url: string
295
+ tag: string
296
+ }
297
+ export interface SortMap {
298
+ [key: string]: SortType
299
+ }
300
+ export function getSortString(field: string, sort: Sort): string {
301
+ if (field === sort.field) {
302
+ return sort.type === "-" ? field : "-" + field
303
+ }
304
+ return field
305
+ }
306
+ export function buildSort(s?: string): Sort {
307
+ if (!s || s.indexOf(",") >= 0) {
308
+ return {} as Sort
309
+ }
310
+ if (s.startsWith("-")) {
311
+ return { field: s.substring(1), type: "-" }
312
+ } else {
313
+ return { field: s.startsWith("+") ? s.substring(1) : s, type: "+" }
314
+ }
315
+ }
316
+ export function buildSortFromRequest(req: Request): Sort {
317
+ const s = query(req, resources.sort)
318
+ return buildSort(s)
319
+ }
320
+ export function renderSort(field: string, sort: Sort): string {
321
+ if (field === sort.field) {
322
+ return sort.type === "-" ? "<i class='sort-down'></i>" : "<i class='sort-up'></i>"
323
+ }
324
+ return et
325
+ }
326
+ export function buildSortSearch(search: string, fields: string[], sortStr?: string): SortMap {
327
+ const sort = buildSort(sortStr)
328
+ search = removeSort(search)
329
+ let sorts: SortMap = {}
330
+ const prefix = search.length > 0 ? "?" + search + "&" : "?"
331
+ for (let i = 0; i < fields.length; i++) {
332
+ sorts[fields[i]] = {
333
+ url: prefix + resources.sort + "=" + getSortString(fields[i], sort),
334
+ tag: renderSort(fields[i], sort),
335
+ }
336
+ }
337
+ return sorts
338
+ }
339
+ export function clone(obj: any): any {
340
+ if (!obj) {
341
+ return obj
342
+ }
343
+ if (obj instanceof Date) {
344
+ return new Date(obj.getTime())
345
+ }
346
+ if (typeof obj !== "object") {
347
+ return obj
348
+ }
349
+ if (Array.isArray(obj)) {
350
+ const arr = []
351
+ for (const sub of obj) {
352
+ const c = clone(sub)
353
+ arr.push(c)
354
+ }
355
+ return arr
356
+ }
357
+ const x: any = {}
358
+ const keys = Object.keys(obj)
359
+ for (const k of keys) {
360
+ const v = obj[k]
361
+ if (v instanceof Date) {
362
+ x[k] = new Date(v.getTime())
363
+ } else {
364
+ switch (typeof v) {
365
+ case "object":
366
+ x[k] = clone(v)
367
+ break
368
+ default:
369
+ x[k] = v
370
+ break
371
+ }
372
+ }
373
+ }
374
+ return x
375
+ }
376
+ export function cloneFilter<F extends Filter>(obj: F, limit: number, page: number): F {
377
+ const f = clone(obj)
378
+ if (!obj.hasOwnProperty(resources.page)) {
379
+ ;(obj as any)[resources.page] = page
380
+ }
381
+ if (!obj.hasOwnProperty(resources.limit)) {
382
+ ;(obj as any)[resources.limit] = limit
383
+ }
384
+ return f
385
+ }
386
+ export function addSequence<T>(list: T[], offset: number, name?: string): T[] {
387
+ const n = name ? name : "sequence"
388
+ const l = list.length
389
+ for (let i = 0; i < l; i++) {
390
+ ;(list[i] as any)[n] = offset + i + 1
391
+ }
392
+ return list
393
+ }
27
394
  export function jsonResult<T>(res: Response, result: SearchResult<T>, quick?: boolean, fields?: string[], config?: SearchConfig): void {
28
395
  if (quick && fields && fields.length > 0) {
29
- res.status(200).json(toCsv(fields, result));
396
+ res.status(200).json(toCsv(fields, result)).end()
30
397
  } else {
31
- res.status(200).json(buildResult(result, config));
398
+ res.status(200).json(buildResult(result, config)).end()
32
399
  }
33
400
  }
34
401
  export function buildResult<T>(r: SearchResult<T>, conf?: SearchConfig): any {
35
402
  if (!conf) {
36
- return r;
403
+ return r
37
404
  }
38
- const x: any = {};
39
- x[conf.list] = r.list;
40
- x[conf.total] = r.total;
41
- if (r.last) {
42
- x[conf.last] = r.last;
405
+ const x: any = {}
406
+ const li = conf.list ? conf.list : "list"
407
+ x[li] = minimizeArray(r.list)
408
+ const to = conf.total ? conf.total : "total"
409
+ x[to] = r.total
410
+ if (r.nextPageToken && r.nextPageToken.length > 0) {
411
+ const t = conf.token ? conf.token : "token"
412
+ x[t] = r.nextPageToken
43
413
  }
44
- return x;
414
+ return x
45
415
  }
46
- export function initializeConfig(conf: SearchConfig): SearchConfig {
416
+ export function initializeConfig(conf?: SearchConfig): SearchConfig | undefined {
47
417
  if (!conf) {
48
- return undefined;
418
+ return undefined
49
419
  }
50
- const c = {
420
+ const c: SearchConfig = {
421
+ excluding: conf.excluding,
51
422
  list: conf.list,
52
423
  total: conf.total,
53
424
  token: conf.token,
54
- last: conf.last,
55
- quick: conf.csv
56
- };
425
+ csv: conf.csv,
426
+ }
427
+ if (!c.excluding || c.excluding.length === 0) {
428
+ c.excluding = "excluding"
429
+ }
57
430
  if (!c.list || c.list.length === 0) {
58
- c.list = 'list';
431
+ c.list = "list"
59
432
  }
60
433
  if (!c.total || c.total.length === 0) {
61
- c.list = 'total';
434
+ c.total = "total"
435
+ }
436
+ if (!c.token || c.token.length === 0) {
437
+ c.token = "nextPageToken"
438
+ }
439
+ return c
440
+ }
441
+ export function fromRequest<S>(req: Request, arr?: string[]): S {
442
+ const s: any = req.method === "GET" ? fromUrl(req, arr) : req.body
443
+ const page = s[resources.page]
444
+ if (page) {
445
+ if (isNaN(page as any)) {
446
+ s[resources.page] = 1
447
+ } else {
448
+ let n = parseFloat(page)
449
+ if (n < 1) {
450
+ n = 1
451
+ }
452
+ s[resources.page] = n
453
+ }
454
+ } else {
455
+ s[resources.page] = 1
456
+ }
457
+
458
+ const limit = s[resources.limit]
459
+ if (limit) {
460
+ if (isNaN(limit as any)) {
461
+ s[resources.limit] = resources.defaultLimit
462
+ } else {
463
+ let n = parseFloat(limit)
464
+ if (n < 1) {
465
+ n = resources.defaultLimit
466
+ }
467
+ s[resources.limit] = n
468
+ }
469
+ } else {
470
+ s[resources.limit] = resources.defaultLimit
62
471
  }
63
- if (!c.last || c.last.length === 0) {
64
- c.last = 'last';
472
+ if (resources.partial.length > 0) {
473
+ delete s[resources.partial]
65
474
  }
66
- return c;
475
+ return s
67
476
  }
68
- export function fromRequest<S>(req: Request, format?: (s: S) => S): S {
69
- const s = (req.method === 'GET' ? fromUrl(req, format) : fromBody(req, format));
70
- return s;
477
+ export function buildArray(arr?: string[], s0?: string, s1?: string, s2?: string): string[] {
478
+ const r: string[] = []
479
+ if (arr && arr.length > 0) {
480
+ for (const a of arr) {
481
+ r.push(a)
482
+ }
483
+ }
484
+ if (s0 && s0.length > 0) {
485
+ r.push(s0)
486
+ }
487
+ if (s1 && s1.length > 0) {
488
+ r.push(s1)
489
+ }
490
+ if (s2 && s2.length > 0) {
491
+ r.push(s2)
492
+ }
493
+ return r
71
494
  }
72
- export function fromBody<S>(req: Request, format?: (s: S) => S): S {
73
- const s = req.body;
74
- if (!format) {
75
- return s;
495
+ export function fromUrl<S>(req: Request, arr?: string[]): S {
496
+ /*
497
+ if (!fields || fields.length === 0) {
498
+ fields = 'fields';
76
499
  }
77
- return format(s);
500
+ */
501
+ const s: any = {}
502
+ const obj = req.query
503
+ const keys = Object.keys(obj)
504
+ for (const key of keys) {
505
+ if (inArray(key, arr)) {
506
+ const x = (obj[key] as string).split(",")
507
+ setValue(s, key, x)
508
+ } else {
509
+ setValue(s, key, obj[key] as string)
510
+ }
511
+ }
512
+ return s
78
513
  }
79
- export function fromUrl<S>(req: Request, format?: (s: S) => S): S {
80
- const s: any = {};
81
- const obj = req.query;
82
- const keys = Object.keys(obj);
83
- for (const key of keys) {
84
- if (key === 'fields') {
85
- const x = (obj[key] as string).split(',');
86
- s[key] = x;
87
- } else {
88
- s[key] = obj[key];
514
+ export function inArray(s: string, arr?: string[]): boolean {
515
+ if (!arr || arr.length === 0) {
516
+ return false
517
+ }
518
+ for (const a of arr) {
519
+ if (s === a) {
520
+ return true
521
+ }
522
+ }
523
+ return false
524
+ }
525
+ /*
526
+ export function setValue<T>(obj: T, path: string, value: string): void {
527
+ const paths = path.split('.');
528
+ if (paths.length === 1) {
529
+ obj[path] = value;
530
+ } else {
531
+ let current: any = obj;
532
+ const l = paths.length - 1;
533
+ for (let i = 0; i < l; i++) {
534
+ const sub = paths[i];
535
+ if (!obj[sub]) {
536
+ obj[sub] = {};
89
537
  }
538
+ current = obj[sub];
90
539
  }
91
- if (format) {
92
- format(s);
540
+ current[paths[paths.length - 1]] = value;
541
+ }
542
+ }
543
+ */
544
+ export function setValue<T, V>(o: T, key: string, value: V): any {
545
+ const obj: any = o
546
+ let replaceKey = key.replace(/\[/g, ".[").replace(/\.\./g, ".")
547
+ if (replaceKey.indexOf(".") === 0) {
548
+ replaceKey = replaceKey.slice(1, replaceKey.length)
549
+ }
550
+ const keys = replaceKey.split(".")
551
+ const firstKey = keys.shift()
552
+ if (!firstKey) {
553
+ return
554
+ }
555
+ const isArrayKey = /\[([0-9]+)\]/.test(firstKey)
556
+ if (keys.length > 0) {
557
+ const firstKeyValue = obj[firstKey] || {}
558
+ const returnValue = setValue(firstKeyValue, keys.join("."), value)
559
+ return setKey(obj, isArrayKey, firstKey, returnValue)
560
+ }
561
+ return setKey(obj, isArrayKey, firstKey, value)
562
+ }
563
+ const setKey = (_object: any, _isArrayKey: boolean, _key: string, _nextValue: any) => {
564
+ if (_isArrayKey) {
565
+ if (_object.length > _key) {
566
+ _object[_key] = _nextValue
567
+ } else {
568
+ _object.push(_nextValue)
93
569
  }
94
- return s;
570
+ } else {
571
+ _object[_key] = _nextValue
572
+ }
573
+ return _object
95
574
  }
96
575
  export interface Limit {
97
- limit?: number;
98
- skip?: number;
99
- refId?: string;
100
- }
101
- export function getLimit<T>(obj: T): Limit {
102
- let refId = obj['refId'];
103
- if (!refId) {
104
- refId = obj['nextPageToken'];
105
- }
106
- let pageSize = obj['limit'];
107
- if (!pageSize) {
108
- pageSize = obj['pageSize'];
109
- }
110
- if (pageSize && !isNaN(pageSize)) {
111
- const ipageSize = Math.floor(parseFloat(pageSize));
112
- if (ipageSize > 0) {
113
- const skip = obj['skip'];
114
- if (skip && !isNaN(skip)) {
115
- const iskip = Math.floor(parseFloat(skip));
116
- if (iskip >= 0) {
117
- deletePageInfo(obj);
118
- return {limit: ipageSize, skip: iskip};
119
- }
576
+ limit: number
577
+ page?: number
578
+ nextPageToken?: string
579
+ fields?: string[]
580
+ pageOrNextPageToken?: string | number
581
+ }
582
+ export function getParameters<T>(obj: T): Limit {
583
+ const o: any = obj
584
+ let fields: string[] | undefined
585
+ const fs = o[resources.fields]
586
+ if (fs && Array.isArray(fs)) {
587
+ fields = fs
588
+ }
589
+ let nextPageToken: string | undefined = o[resources.nextPageToken]
590
+ let page = 1
591
+ let spage = o[resources.page]
592
+ if (spage && typeof spage === "string") {
593
+ if (!isNaN(spage as any)) {
594
+ const ipage = Math.floor(parseFloat(spage))
595
+ if (ipage > 1) {
596
+ page = ipage
120
597
  }
121
- let pageIndex = obj['page'];
122
- if (!pageIndex) {
123
- pageIndex = obj['pageIndex'];
124
- if (!pageIndex) {
125
- pageIndex = obj['pageNo'];
126
- }
598
+ }
599
+ }
600
+ let limit = resources.defaultLimit
601
+ let slimit = o[resources.limit]
602
+ if (slimit && typeof slimit === "string") {
603
+ if (!isNaN(slimit as any)) {
604
+ const ilimit = Math.floor(parseFloat(slimit))
605
+ if (ilimit > 0) {
606
+ limit = ilimit
127
607
  }
128
- if (pageIndex && !isNaN(pageIndex)) {
129
- let ipageIndex = Math.floor(parseFloat(pageIndex));
130
- if (ipageIndex < 1) {
131
- ipageIndex = 1;
132
- }
133
- let firstPageSize = obj['firstLimit'];
134
- if (!firstPageSize) {
135
- firstPageSize = obj['firstPageSize'];
136
- }
137
- if (!firstPageSize) {
138
- firstPageSize = obj['initPageSize'];
139
- }
140
- if (firstPageSize && !isNaN(firstPageSize)) {
141
- const ifirstPageSize = Math.floor(parseFloat(firstPageSize));
142
- if (ifirstPageSize > 0) {
143
- deletePageInfo(obj);
144
- return {limit: ipageSize, skip: ipageSize * (ipageIndex - 2) + ifirstPageSize};
145
- }
146
- }
147
- deletePageInfo(obj);
148
- return {limit: ipageSize, skip: ipageSize * (ipageIndex - 1)};
608
+ }
609
+ }
610
+ const r: Limit = { limit: limit, fields, page, nextPageToken, pageOrNextPageToken: page }
611
+ if (r.nextPageToken && r.nextPageToken.length > 0) {
612
+ r.pageOrNextPageToken = r.nextPageToken
613
+ }
614
+ deletePageInfo(o)
615
+ return r
616
+ }
617
+ // tslint:disable-next-line:array-type
618
+ export function deletePageInfo(obj: any, arr?: Array<string | undefined>): void {
619
+ if (!arr || arr.length === 0) {
620
+ delete obj[resources.fields]
621
+ delete obj[resources.limit]
622
+ delete obj[resources.page]
623
+ if (resources.nextPageToken && resources.nextPageToken.length > 0) {
624
+ delete obj[resources.nextPageToken]
625
+ }
626
+ if (resources.partial && resources.partial.length > 0) {
627
+ delete obj[resources.partial]
628
+ }
629
+ } else {
630
+ for (const o of arr) {
631
+ if (o && o.length > 0) {
632
+ delete obj[o]
149
633
  }
150
- deletePageInfo(obj);
151
- return {limit: ipageSize, skip: 0, refId};
152
- }
153
- }
154
- return {limit: undefined, skip: undefined, refId};
155
- }
156
- export function deletePageInfo(obj: any): void {
157
- delete obj['limit'];
158
- delete obj['firstLimit'];
159
- delete obj['skip'];
160
- delete obj['page'];
161
- delete obj['pageNo'];
162
- delete obj['pageIndex'];
163
- delete obj['pageSize'];
164
- delete obj['initPageSize'];
165
- delete obj['firstPageSize'];
166
- delete obj['refId'];
167
- delete obj['refId'];
168
- delete obj['nextPageToken'];
169
- }
170
- const re = /"/g;
634
+ }
635
+ }
636
+ }
637
+ const re = /"/g
171
638
  export function toCsv<T>(fields: string[], r: SearchResult<T>): string {
172
639
  if (!r || r.list.length === 0) {
173
- return '0';
640
+ return "0"
174
641
  } else {
175
- const e = '';
176
- const s = 'string';
177
- const n = 'number';
178
- const b = '""';
179
- const rows: string[] = [];
180
- rows.push('' + r.total + ',' + (r.last ? '1' : ''));
642
+ const e = ""
643
+ const s = "string"
644
+ const n = "number"
645
+ const b = '""'
646
+ const rows: string[] = []
647
+ rows.push("" + (r.total ? r.total : "") + "," + (r.nextPageToken ? r.nextPageToken : ""))
181
648
  for (const item of r.list) {
182
- const cols: string[] = [];
649
+ const cols: string[] = []
183
650
  for (const name of fields) {
184
- const v = item[name];
651
+ const v = (item as any)[name]
185
652
  if (!v) {
186
- cols.push(e);
653
+ cols.push(e)
187
654
  } else {
188
655
  if (typeof v === s) {
189
- if (s.indexOf(',') >= 0) {
190
- cols.push('"' + v.replace(re, b) + '"');
656
+ if (s.indexOf(",") >= 0) {
657
+ cols.push('"' + v.replace(re, b) + '"')
191
658
  } else {
192
- cols.push(v);
659
+ cols.push(v)
193
660
  }
194
661
  } else if (v instanceof Date) {
195
- cols.push(v.toISOString());
662
+ cols.push(v.toISOString())
196
663
  } else if (typeof v === n) {
197
- cols.push(v.toString());
664
+ cols.push(v.toString())
198
665
  } else {
199
- cols.push('');
666
+ cols.push("")
200
667
  }
201
668
  }
202
669
  }
203
- rows.push(cols.join(','));
670
+ rows.push(cols.join(","))
204
671
  }
205
- return rows.join('\n');
672
+ return rows.join("\n")
206
673
  }
207
674
  }
208
- /*
209
- export interface SearchModelBuilder<S extends SearchModel> {
210
- buildFromRequestUrl(req: Request): S;
211
- buildFromRequestBody(req: Request): S;
212
- }
213
- export class DefaultSearchModelBuilder<S extends SearchModel> implements SearchModelBuilder<S> {
214
- constructor(protected format: (s: S) => S) {
215
- }
216
- buildFromRequestUrl(req: Request): S {
217
- const s: any = {};
218
- const obj = req.query;
219
- const keys = Object.keys(obj);
220
- for (const key of keys) {
221
- if (key === 'fields') {
222
- const x = (obj[key] as string).split(',');
223
- s[key] = x;
224
- } else {
225
- s[key] = obj[key];
226
- }
227
- }
228
- formatSearchModel(s);
229
- if (this.format) {
230
- this.format(s);
675
+
676
+ export interface DateRange {
677
+ startDate?: Date
678
+ endDate?: Date
679
+ startTime?: Date
680
+ endTime?: Date
681
+ min?: Date
682
+ max?: Date
683
+ upper?: Date
684
+ }
685
+ export interface NumberRange {
686
+ min?: number
687
+ max?: number
688
+ lower?: number
689
+ upper?: number
690
+ }
691
+ export interface Metadata {
692
+ dates?: string[]
693
+ numbers?: string[]
694
+ }
695
+ export function buildMetadata(attributes: Attributes, includeDate?: boolean): Metadata {
696
+ const keys: string[] = Object.keys(attributes)
697
+ const dates: string[] = []
698
+ const numbers: string[] = []
699
+ for (const key of keys) {
700
+ const attr: Attribute = attributes[key]
701
+ if (attr.type === "number" || attr.type === "integer") {
702
+ numbers.push(key)
703
+ } else if (attr.type === "datetime" || (includeDate === true && attr.type === "date")) {
704
+ dates.push(key)
231
705
  }
232
- return s;
233
706
  }
234
- buildFromRequestBody(req: Request): S {
235
- const s = req.body;
236
- formatSearchModel(s);
237
- if (this.format) {
238
- this.format(s);
239
- }
240
- return s;
707
+ const m: Metadata = {}
708
+ if (dates.length > 0) {
709
+ m.dates = dates
710
+ }
711
+ if (numbers.length > 0) {
712
+ m.numbers = numbers
241
713
  }
714
+ return m
242
715
  }
243
- export function formatSearchModel<S extends SearchModel>(s: S): S {
244
- if (!s.sort) {
245
- s.sort = '';
716
+
717
+ const _datereg = "/Date("
718
+ const _re = /-?\d+/
719
+ function toDate(v: any): Date | null | undefined {
720
+ if (!v) {
721
+ return null
246
722
  }
247
- const pageSize: any = (s.hasOwnProperty('limit' ) ? s.limit : 0);
248
- const page: any = s.page;
249
- const initPageSize: any = (!s.hasOwnProperty('firstLimit') || !s.firstLimit ? pageSize : s.firstLimit);
250
- if (!!pageSize) {
251
- s.limit = (isNaN(pageSize) ? 0 : Math.floor(parseFloat(pageSize)));
723
+ if (v instanceof Date) {
724
+ return v
725
+ } else if (typeof v === "number") {
726
+ return new Date(v)
252
727
  }
253
- if (!!page) {
254
- s.page = (isNaN(page) ? 0 : Math.floor(parseFloat(page)));
728
+ const i = v.indexOf(_datereg)
729
+ if (i >= 0) {
730
+ const m = _re.exec(v)
731
+ if (m !== null) {
732
+ const d = parseInt(m[0], 10)
733
+ return new Date(d)
734
+ } else {
735
+ return null
736
+ }
737
+ } else {
738
+ if (isNaN(v)) {
739
+ return new Date(v)
740
+ } else {
741
+ const d = parseInt(v, 10)
742
+ return new Date(d)
743
+ }
744
+ }
745
+ }
746
+
747
+ export function format<T>(obj: T, dates?: string[], nums?: string[]): T {
748
+ const o: any = obj
749
+ if (dates && dates.length > 0) {
750
+ for (const s of dates) {
751
+ const v = o[s]
752
+ if (v) {
753
+ if (v instanceof Date) {
754
+ continue
755
+ }
756
+ if (typeof v === "string" || typeof v === "number") {
757
+ const d = toDate(v)
758
+ if (d) {
759
+ if (!(d instanceof Date) || d.toString() === "Invalid Date") {
760
+ delete o[s]
761
+ } else {
762
+ o[s] = d
763
+ }
764
+ }
765
+ } else if (typeof v === "object") {
766
+ const keys = Object.keys(v)
767
+ for (const key of keys) {
768
+ const v2 = v[key]
769
+ if (v2 instanceof Date) {
770
+ continue
771
+ }
772
+ if (typeof v2 === "string" || typeof v2 === "number") {
773
+ const d2 = toDate(v2)
774
+ if (d2) {
775
+ if (!(d2 instanceof Date) || d2.toString() === "Invalid Date") {
776
+ delete v[key]
777
+ } else {
778
+ v[key] = d2
779
+ }
780
+ }
781
+ }
782
+ }
783
+ }
784
+ }
785
+ }
255
786
  }
256
- if (!!initPageSize) {
257
- s.firstLimit = (isNaN(initPageSize) ? 0 : Math.floor(parseFloat(initPageSize)));
787
+ if (nums && nums.length > 0) {
788
+ for (const s of nums) {
789
+ const v = o[s]
790
+ if (v) {
791
+ if (v instanceof Date) {
792
+ delete o[s]
793
+ continue
794
+ }
795
+ if (typeof v === "number") {
796
+ continue
797
+ }
798
+ if (typeof v === "string") {
799
+ if (!isNaN(v as any)) {
800
+ delete o[s]
801
+ continue
802
+ } else {
803
+ const i = parseFloat(v)
804
+ o[s] = i
805
+ }
806
+ } else if (typeof v === "object") {
807
+ const keys = Object.keys(v)
808
+ for (const key of keys) {
809
+ const v2 = v[key]
810
+ if (v2 instanceof Date) {
811
+ delete o[key]
812
+ continue
813
+ }
814
+ if (typeof v2 === "number") {
815
+ continue
816
+ }
817
+ if (typeof v2 === "string") {
818
+ if (!isNaN(v2 as any)) {
819
+ delete v[key]
820
+ } else {
821
+ const i = parseFloat(v2)
822
+ v[key] = i
823
+ }
824
+ }
825
+ }
826
+ }
827
+ }
828
+ }
258
829
  }
259
- if (!s.firstLimit) {
260
- s.firstLimit = s.limit;
830
+ return o
831
+ }
832
+ export function getMetadataFunc<T, ID>(
833
+ viewService: ViewService<T, ID> | ((id: ID, ctx?: any) => Promise<T>),
834
+ dates?: string[],
835
+ numbers?: string[],
836
+ keys?: Attributes | Attribute[] | string[],
837
+ ): Metadata | undefined {
838
+ const m: Metadata = { dates, numbers }
839
+ if ((m.dates && m.dates.length > 0) || (m.numbers && m.numbers.length > 0)) {
840
+ return m
261
841
  }
262
- if (!s.hasOwnProperty('page') || !s.page || s.page < 1) {
263
- s.page = 1;
842
+ if (keys) {
843
+ if (!Array.isArray(keys)) {
844
+ return buildMetadata(keys)
845
+ }
264
846
  }
265
- if (!s.hasOwnProperty('keyword') || !s.keyword) {
266
- s.keyword = '';
847
+ if (typeof viewService !== "function" && viewService.metadata) {
848
+ const metadata = viewService.metadata()
849
+ if (metadata) {
850
+ return buildMetadata(metadata)
851
+ }
267
852
  }
268
- return s;
853
+ return undefined
269
854
  }
270
- */