-
Notifications
You must be signed in to change notification settings - Fork 20
/
filters.ts
111 lines (94 loc) · 2.46 KB
/
filters.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import type {
IntrospectionNamedTypeRef,
IntrospectionObjectType,
IntrospectionNonNullTypeRef,
IntrospectionType,
} from 'graphql'
import type { Filter, FilterMap, FilterSpec, Operator } from './types'
/**
* Transforms for a certain field type a search value for a field (the key)
* to a filter that is understood by postgraphile.
*
* For example:
*
* - type: {kind: "SCALAR", name: "String", ofType: null, __typename: "__Type"}
* - value: "some keyword"
* - key: "name"
*
* Is transformed to:
*
* ```ts
* {
* name: {
* includes: "some keyword"
* }
* }
* ```
*/
export const mapFilterType = (
type: IntrospectionNamedTypeRef,
value: any,
key: string
): Filter | undefined => {
if (Array.isArray(value)) {
const spec: FilterSpec = {
operator: 'in',
value,
}
value = spec
}
if (typeof value !== 'object') {
const typeName = (type?.name ?? '').toLowerCase()
let operator: Operator = 'equalTo'
// string uses includes as the default operator for historical reasons
if (typeName === 'string') {
operator = 'includes'
}
// a type of FullText uses matches as the default operator for historical reasons
if (typeName === 'fulltext') {
operator = 'matches'
value = `${value}:*`
}
const spec: FilterSpec = {
operator,
value,
}
value = spec
}
const { operator, value: v, key: filterKey } = value
// react-admin sends the value as undefined when the filter is cleared
// rather than making every parse function handle that, deal with it here
if (v === undefined) {
return undefined
}
return {
[filterKey || key]:
typeof operator !== 'undefined'
? {
[operator]: v,
}
: v,
}
}
export const createFilter = (
fields: { [key: string]: unknown },
type: IntrospectionType
): FilterMap | undefined => {
const empty: Filter[] = []
const filters = Object.keys(fields).reduce((next, key) => {
const maybeType = (type as IntrospectionObjectType).fields.find((f: any) => f.name === key)
if (maybeType) {
const thisType = (maybeType.type as IntrospectionNonNullTypeRef).ofType || maybeType.type
const filter = mapFilterType(thisType as IntrospectionNamedTypeRef, fields[key], key)
if (filter === undefined) {
return next
}
return [...next, filter]
}
return next
}, empty)
if (filters === empty) {
return undefined
}
return { and: filters }
}