@things-factory/shell 5.0.0-alpha.4 → 5.0.0-alpha.42

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
- import { Between, Equal, ILike, In, IsNull, Like, Not, Raw } from 'typeorm'
1
+ import { Between, Equal, FindOperator, ILike, In, IsNull, Like, Not, Raw } from 'typeorm'
2
2
 
3
- import { ListParam } from '../service/common-types'
3
+ import { Filter, ListParam, Pagination, Sorting } from '../service/common-types'
4
4
 
5
- const OPERATION_FUNCTION_MAP = {
5
+ const OPERATION_FUNCTION_MAP: { [operator: string]: (value: any) => FindOperator<any> } = {
6
6
  search: value => ILike(value),
7
7
  eq: value => Equal(value),
8
8
  noteq: value => Not(Equal(value)),
@@ -27,12 +27,12 @@ const OPERATION_FUNCTION_MAP = {
27
27
  between: value => Between(value[0], value[1])
28
28
  }
29
29
 
30
- function getOperatorFunction({ operator, name, value, dataType }) {
30
+ function getOperatorFunction({ operator, value }: { operator: string; value: any }): FindOperator<any> {
31
31
  return OPERATION_FUNCTION_MAP[operator](value)
32
32
  }
33
33
 
34
- function makePaginationParams(pagination) {
35
- var jsonParams = {}
34
+ function makePaginationParams(pagination: Pagination): { skip?: number; take?: number } {
35
+ var result = {} as { skip?: number; take?: number }
36
36
  if (pagination) {
37
37
  var { page = 0, limit = 0 } = pagination
38
38
  var skip = 0
@@ -41,39 +41,41 @@ function makePaginationParams(pagination) {
41
41
  if (limit > 0) {
42
42
  skip = Math.max(page - 1, 0) * limit
43
43
  take = limit
44
- Object.assign(jsonParams, {
44
+ Object.assign(result, {
45
45
  skip,
46
46
  take
47
47
  })
48
48
  }
49
49
  }
50
50
 
51
- return jsonParams
51
+ return result
52
52
  }
53
53
 
54
- function makeSortingParams(sortings) {
55
- var jsonParams = {}
54
+ function makeSortingParams(sortings: Sorting[]): { order?: { [name: string]: 'DESC' | 'ASC' } } {
55
+ var result = {} as { order?: { [name: string]: 'DESC' | 'ASC' } }
56
56
  if (sortings) {
57
- var order = {}
57
+ var order = {} as { [name: string]: 'DESC' | 'ASC' }
58
58
  sortings.forEach(s => {
59
59
  order[s.name] = s.desc ? 'DESC' : 'ASC'
60
60
  })
61
61
 
62
- Object.assign(jsonParams, {
62
+ Object.assign(result, {
63
63
  order
64
64
  })
65
65
  }
66
66
 
67
- return jsonParams
67
+ return result
68
68
  }
69
69
 
70
- function makeFilterParams(filters) {
70
+ function makeFilterParams(filters: Filter[]): {
71
+ where: { [name: string]: FindOperator<any> } | { [name: string]: FindOperator<any> }[]
72
+ } {
71
73
  /* for where AND clauses */
72
74
  const columnFilters = filters.filter(filter => filter.operator !== 'search')
73
75
  const columnWhere = columnFilters.reduce((where, f) => {
74
76
  where[f.name] = getOperatorFunction(f)
75
77
  return where
76
- }, {})
78
+ }, {} as { [name: string]: FindOperator<any> })
77
79
 
78
80
  const searchFilters = filters.filter(filter => filter.operator === 'search')
79
81
  if (searchFilters.length === 0) {
@@ -95,9 +97,19 @@ function makeFilterParams(filters) {
95
97
  }
96
98
  }
97
99
 
98
- export function convertListParams(params: typeof ListParam, domain?: String) {
99
- var { pagination, filters = [], sortings } = params as any
100
- var jsonParams = {}
100
+ export function convertListParams(
101
+ params: ListParam,
102
+ domain?: String
103
+ ): {
104
+ where?: { [name: string]: FindOperator<any> }
105
+ order?: {
106
+ [name: string]: 'DESC' | 'ASC'
107
+ }
108
+ skip?: number
109
+ take?: number
110
+ } {
111
+ var { pagination, filters = [], sortings } = params
112
+ var result = {}
101
113
 
102
114
  if (domain) {
103
115
  filters.push({
@@ -107,9 +119,9 @@ export function convertListParams(params: typeof ListParam, domain?: String) {
107
119
  })
108
120
  }
109
121
 
110
- if (pagination) Object.assign(jsonParams, makePaginationParams(pagination))
111
- if (sortings) Object.assign(jsonParams, makeSortingParams(sortings))
112
- if (filters) Object.assign(jsonParams, makeFilterParams(filters))
122
+ if (pagination) Object.assign(result, makePaginationParams(pagination))
123
+ if (sortings) Object.assign(result, makeSortingParams(sortings))
124
+ if (filters) Object.assign(result, makeFilterParams(filters))
113
125
 
114
- return jsonParams
126
+ return result
115
127
  }
@@ -1,6 +1,9 @@
1
- import { Brackets } from 'typeorm'
1
+ import { Brackets, Repository, SelectQueryBuilder } from 'typeorm'
2
+ import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata'
2
3
 
4
+ import { Domain } from '../service/domain/domain'
3
5
  import { buildCondition } from './condition-builder'
6
+ import { buildWhereClause } from './where-clause-builder'
4
7
 
5
8
  export const buildQuery = function (queryBuilder: any, params: any, context: any, domainRef: Boolean = true) {
6
9
  const columnFilters = params.filters?.filter(filter => filter.operator !== 'search') || []
@@ -58,3 +61,92 @@ export const buildQuery = function (queryBuilder: any, params: any, context: any
58
61
  })
59
62
  }
60
63
  }
64
+
65
+ export function getQueryBuilderFromListParams<Type>(options: {
66
+ repository: Repository<Type>
67
+ params: any
68
+ domain?: Domain
69
+ alias?: string
70
+ }): SelectQueryBuilder<Type> {
71
+ var { repository, params, domain, alias } = options
72
+ const selectQueryBuilder = repository.createQueryBuilder(alias)
73
+ alias = selectQueryBuilder.alias
74
+
75
+ const columnFilters = params.filters?.filter(filter => filter.operator !== 'search') || []
76
+ const searchFilters = params.filters?.filter(filter => filter.operator === 'search') || []
77
+ const pagination = params.pagination
78
+ const sortings = params.sortings
79
+
80
+ const metadata = repository.metadata
81
+ const columnMetas = params.filters
82
+ .map(filter => filter.name)
83
+ .reduce((sum, name) => {
84
+ sum[name] = metadata.columns.find(column => column.propertyName === name)
85
+ return sum
86
+ }, {} as { [name: string]: ColumnMetadata })
87
+
88
+ if (columnFilters && columnFilters.length > 0) {
89
+ columnFilters.forEach(filter => {
90
+ const { name, operator, value } = filter
91
+
92
+ const condition = buildWhereClause({
93
+ alias,
94
+ columnMeta: columnMetas[name],
95
+ operator,
96
+ value,
97
+ seq: Object.keys(selectQueryBuilder.getParameters()).length + 1,
98
+ domain,
99
+ selectQueryBuilder
100
+ })
101
+
102
+ if (condition?.clause) selectQueryBuilder.andWhere(condition.clause)
103
+ if (condition?.parameters) selectQueryBuilder.setParameters(condition.parameters)
104
+ })
105
+ }
106
+
107
+ if (searchFilters.length > 0) {
108
+ selectQueryBuilder.andWhere(
109
+ new Brackets(qb => {
110
+ searchFilters.forEach(filter => {
111
+ const { name, operator, value } = filter
112
+
113
+ const condition = buildWhereClause({
114
+ alias,
115
+ columnMeta: columnMetas[name],
116
+ operator /* has to be 'search' */,
117
+ value,
118
+ seq: Object.keys(selectQueryBuilder.getParameters()).length + 1,
119
+ domain,
120
+ selectQueryBuilder
121
+ })
122
+
123
+ if (condition?.clause) selectQueryBuilder.orWhere(condition.clause)
124
+ if (condition?.parameters) selectQueryBuilder.setParameters(condition.parameters)
125
+ })
126
+ })
127
+ )
128
+ }
129
+
130
+ if (domain) {
131
+ selectQueryBuilder.andWhere(`${selectQueryBuilder.alias}.domain = :domain`, { domain: domain.id })
132
+ }
133
+
134
+ if (pagination && pagination.page > 0 && pagination.limit > 0) {
135
+ selectQueryBuilder.skip(pagination.limit * (pagination.page - 1))
136
+ selectQueryBuilder.take(pagination.limit)
137
+ }
138
+
139
+ if (sortings && sortings.length > 0) {
140
+ sortings.forEach((sorting, index) => {
141
+ const sortField =
142
+ sorting.name.split('.').length > 1 ? sorting.name : `${selectQueryBuilder.alias}.${sorting.name}`
143
+ if (index === 0) {
144
+ selectQueryBuilder.orderBy(sortField, sorting.desc ? 'DESC' : 'ASC')
145
+ } else {
146
+ selectQueryBuilder.addOrderBy(sortField, sorting.desc ? 'DESC' : 'ASC')
147
+ }
148
+ })
149
+ }
150
+
151
+ return selectQueryBuilder
152
+ }
@@ -0,0 +1,200 @@
1
+ import { SelectQueryBuilder } from 'typeorm'
2
+ import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata'
3
+
4
+ import { Domain } from '../service/domain/domain'
5
+
6
+ function getClause(
7
+ selectQueryBuilder: SelectQueryBuilder<any>,
8
+ columnMeta: ColumnMetadata,
9
+ operator: string,
10
+ field: string,
11
+ pname: string
12
+ ): string {
13
+ const relation = columnMeta.relationMetadata
14
+
15
+ if (!relation) {
16
+ switch (operator) {
17
+ case 'like':
18
+ return `${field} LIKE :${pname}`
19
+
20
+ case 'search':
21
+ case 'i_like':
22
+ return `LOWER(${field}) LIKE :${pname}`
23
+
24
+ case 'nlike':
25
+ return `${field} NOT LIKE :${pname}`
26
+
27
+ case 'i_nlike':
28
+ return `LOWER(${field}) NOT LIKE :${pname}`
29
+ }
30
+ }
31
+
32
+ const inverseEntityMetadata = relation.inverseEntityMetadata
33
+ const { target, tableName, ownColumns } = inverseEntityMetadata
34
+ var subquery = selectQueryBuilder.subQuery().select('id').from(target, tableName)
35
+
36
+ switch (operator) {
37
+ case 'like':
38
+ subquery = subquery.where(`${tableName}.name LIKE :${pname}`)
39
+ break
40
+
41
+ case 'search':
42
+ case 'i_like':
43
+ subquery = subquery.where(`LOWER(${tableName}.name) LIKE :${pname}`)
44
+ break
45
+
46
+ case 'nlike':
47
+ subquery = subquery.where(`${tableName}.name NOT LIKE :${pname}`)
48
+ break
49
+
50
+ case 'i_nlike':
51
+ subquery = subquery.where(`LOWER(${tableName}.name) NOT LIKE :${pname}`)
52
+ break
53
+ }
54
+
55
+ if (ownColumns.find(column => column.propertyName === 'domain')) {
56
+ subquery.andWhere(`${tableName}.domain_id = :domain`)
57
+ }
58
+
59
+ return `${field} IN ${subquery.getQuery()}`
60
+ }
61
+
62
+ export const buildWhereClause = function (options: {
63
+ alias: string
64
+ columnMeta: ColumnMetadata
65
+ operator: string
66
+ value: any
67
+ seq: number
68
+ domain: Domain
69
+ selectQueryBuilder: SelectQueryBuilder<any>
70
+ }) {
71
+ const { alias, columnMeta, operator, value, seq, selectQueryBuilder, domain } = options
72
+ const values = value instanceof Array ? value : [value]
73
+ const { propertyName: name, propertyAliasName, propertyPath: path, databaseName } = columnMeta
74
+ const field = `${alias}.${databaseName}`
75
+ const pname = `args${seq}`
76
+
77
+ switch (operator) {
78
+ case 'eq':
79
+ return {
80
+ clause: `${field} = :${pname}`,
81
+ parameters: { [pname]: value }
82
+ }
83
+
84
+ case 'like':
85
+ return {
86
+ clause: getClause(selectQueryBuilder, columnMeta, operator, field, pname),
87
+ parameters: { [pname]: `%${value}%` }
88
+ }
89
+
90
+ case 'search':
91
+ case 'i_like':
92
+ return {
93
+ clause: getClause(selectQueryBuilder, columnMeta, operator, field, pname),
94
+ parameters: { [pname]: `%${String(value).toLowerCase()}%` }
95
+ }
96
+
97
+ case 'nlike':
98
+ return {
99
+ clause: getClause(selectQueryBuilder, columnMeta, operator, field, pname),
100
+ value: { [pname]: `%${value}%` }
101
+ }
102
+
103
+ case 'i_nlike':
104
+ return {
105
+ clause: getClause(selectQueryBuilder, columnMeta, operator, field, pname),
106
+ value: { [pname]: `%${String(value).toLowerCase()}%` }
107
+ }
108
+
109
+ case 'lt':
110
+ return {
111
+ clause: `${field} < :${pname}`,
112
+ parameters: { [pname]: value }
113
+ }
114
+
115
+ case 'gt':
116
+ return {
117
+ clause: `${field} > :${pname}`,
118
+ parameters: { [pname]: value }
119
+ }
120
+
121
+ case 'lte':
122
+ return {
123
+ clause: `${field} <= :${pname}`,
124
+ parameters: { [pname]: value }
125
+ }
126
+
127
+ case 'gte':
128
+ return {
129
+ clause: `${field} >= :${pname}`,
130
+ parameters: { [pname]: value }
131
+ }
132
+
133
+ case 'noteq':
134
+ return {
135
+ clause: `${field} != :${pname}`,
136
+ parameters: { [pname]: value }
137
+ }
138
+
139
+ case 'in':
140
+ return {
141
+ clause: `${field} IN (:...${pname})`,
142
+ parameters: { [pname]: values }
143
+ }
144
+
145
+ case 'notin':
146
+ return {
147
+ clause: `${field} NOT IN (:...${pname})`,
148
+ parameters: { [pname]: values }
149
+ }
150
+
151
+ case 'notin_with_null':
152
+ return {
153
+ clause: `${field} IS NULL OR ${field} NOT IN (:...${pname}))`,
154
+ parameters: { [pname]: values }
155
+ }
156
+
157
+ case 'is_null':
158
+ return {
159
+ clause: `${field} IS NULL`
160
+ }
161
+ case 'is_not_null':
162
+ return {
163
+ clause: `${field} IS NOT NULL`
164
+ }
165
+ case 'is_false':
166
+ return {
167
+ clause: `${field} IS FALSE`
168
+ }
169
+ case 'is_true':
170
+ return {
171
+ clause: `${field} IS TRUE`
172
+ }
173
+ case 'is_not_false':
174
+ return {
175
+ clause: `${field} IS NOT FALSE`
176
+ }
177
+ case 'is_not_true':
178
+ return {
179
+ clause: `${field} IS NOT TRUE`
180
+ }
181
+ case 'is_present':
182
+ return {
183
+ clause: `${field} IS PRESENT`
184
+ }
185
+ case 'is_blank':
186
+ return {
187
+ clause: `${field} IS BLANK`
188
+ }
189
+ case 'is_empty_num_id':
190
+ return {
191
+ clause: `${field} IS EMPTY NUMERIC ID`
192
+ }
193
+
194
+ case 'between':
195
+ return {
196
+ clause: `${field} BETWEEN :${pname}_1 AND :${pname}_2`,
197
+ parameters: { [`args${seq}_1`]: values[0], [`args${seq}_2`]: values[1] }
198
+ }
199
+ }
200
+ }