@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.
- package/client/themes/tooltip-theme.css +4 -3
- package/config/config.production.js +13 -0
- package/dist-server/middlewares/index.js +2 -2
- package/dist-server/middlewares/index.js.map +1 -1
- package/dist-server/pubsub.js +2 -2
- package/dist-server/pubsub.js.map +1 -1
- package/dist-server/server-dev.js +21 -15
- package/dist-server/server-dev.js.map +1 -1
- package/dist-server/server.js +18 -13
- package/dist-server/server.js.map +1 -1
- package/dist-server/service/domain/domain.js +34 -1
- package/dist-server/service/domain/domain.js.map +1 -1
- package/dist-server/utils/condition-builder.js +10 -11
- package/dist-server/utils/condition-builder.js.map +1 -1
- package/dist-server/utils/list-params-converter.js +12 -12
- package/dist-server/utils/list-params-converter.js.map +1 -1
- package/dist-server/utils/list-query-builder.js +77 -1
- package/dist-server/utils/list-query-builder.js.map +1 -1
- package/dist-server/utils/where-clause-builder.js +159 -0
- package/dist-server/utils/where-clause-builder.js.map +1 -0
- package/package.json +25 -25
- package/server/middlewares/index.ts +3 -2
- package/server/pubsub.ts +3 -3
- package/server/server-dev.ts +32 -24
- package/server/server.ts +29 -21
- package/server/service/domain/domain.ts +37 -2
- package/server/utils/condition-builder.ts +12 -12
- package/server/utils/list-params-converter.ts +34 -22
- package/server/utils/list-query-builder.ts +93 -1
- package/server/utils/where-clause-builder.ts +200 -0
@@ -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,
|
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
|
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(
|
44
|
+
Object.assign(result, {
|
45
45
|
skip,
|
46
46
|
take
|
47
47
|
})
|
48
48
|
}
|
49
49
|
}
|
50
50
|
|
51
|
-
return
|
51
|
+
return result
|
52
52
|
}
|
53
53
|
|
54
|
-
function makeSortingParams(sortings) {
|
55
|
-
var
|
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(
|
62
|
+
Object.assign(result, {
|
63
63
|
order
|
64
64
|
})
|
65
65
|
}
|
66
66
|
|
67
|
-
return
|
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(
|
99
|
-
|
100
|
-
|
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(
|
111
|
-
if (sortings) Object.assign(
|
112
|
-
if (filters) Object.assign(
|
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
|
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
|
+
}
|