@subsquid/openreader 4.6.0 → 5.0.0-beta.62d187
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/context.d.ts +2 -2
- package/lib/context.d.ts.map +1 -1
- package/lib/db.d.ts +3 -3
- package/lib/db.d.ts.map +1 -1
- package/lib/db.js +2 -2
- package/lib/db.js.map +1 -1
- package/lib/dialect/common.d.ts +19 -0
- package/lib/dialect/common.d.ts.map +1 -0
- package/lib/dialect/common.js +42 -0
- package/lib/dialect/common.js.map +1 -0
- package/lib/dialect/index.d.ts +6 -0
- package/lib/dialect/index.d.ts.map +1 -0
- package/lib/dialect/index.js +49 -0
- package/lib/dialect/index.js.map +1 -0
- package/lib/{opencrud → dialect/opencrud}/orderBy.d.ts +2 -3
- package/lib/dialect/opencrud/orderBy.d.ts.map +1 -0
- package/lib/{opencrud → dialect/opencrud}/orderBy.js +4 -21
- package/lib/dialect/opencrud/orderBy.js.map +1 -0
- package/lib/{opencrud → dialect/opencrud}/schema.d.ts +2 -6
- package/lib/dialect/opencrud/schema.d.ts.map +1 -0
- package/lib/{opencrud → dialect/opencrud}/schema.js +21 -34
- package/lib/dialect/opencrud/schema.js.map +1 -0
- package/lib/{opencrud → dialect/opencrud}/tree.d.ts +3 -3
- package/lib/dialect/opencrud/tree.d.ts.map +1 -0
- package/lib/{opencrud → dialect/opencrud}/tree.js +3 -3
- package/lib/dialect/opencrud/tree.js.map +1 -0
- package/lib/{opencrud → dialect/opencrud}/where.d.ts +1 -1
- package/lib/dialect/opencrud/where.d.ts.map +1 -0
- package/lib/{opencrud → dialect/opencrud}/where.js +4 -13
- package/lib/dialect/opencrud/where.js.map +1 -0
- package/lib/dialect/thegraph/locale.d.ts +2 -0
- package/lib/dialect/thegraph/locale.d.ts.map +1 -0
- package/lib/dialect/thegraph/locale.js +49 -0
- package/lib/dialect/thegraph/locale.js.map +1 -0
- package/lib/dialect/thegraph/orderBy.d.ts +11 -0
- package/lib/dialect/thegraph/orderBy.d.ts.map +1 -0
- package/lib/dialect/thegraph/orderBy.js +71 -0
- package/lib/dialect/thegraph/orderBy.js.map +1 -0
- package/lib/dialect/thegraph/schema.d.ts +25 -0
- package/lib/dialect/thegraph/schema.d.ts.map +1 -0
- package/lib/dialect/thegraph/schema.js +429 -0
- package/lib/dialect/thegraph/schema.js.map +1 -0
- package/lib/dialect/thegraph/tree.d.ts +10 -0
- package/lib/dialect/thegraph/tree.d.ts.map +1 -0
- package/lib/dialect/thegraph/tree.js +148 -0
- package/lib/dialect/thegraph/tree.js.map +1 -0
- package/lib/dialect/thegraph/where.d.ts +9 -0
- package/lib/dialect/thegraph/where.d.ts.map +1 -0
- package/lib/dialect/thegraph/where.js +188 -0
- package/lib/dialect/thegraph/where.js.map +1 -0
- package/lib/ir/args.d.ts +1 -1
- package/lib/ir/args.d.ts.map +1 -1
- package/lib/main.js +1 -1
- package/lib/main.js.map +1 -1
- package/lib/model.d.ts +2 -0
- package/lib/model.d.ts.map +1 -1
- package/lib/model.schema.d.ts.map +1 -1
- package/lib/model.schema.js +31 -2
- package/lib/model.schema.js.map +1 -1
- package/lib/server.d.ts +3 -1
- package/lib/server.d.ts.map +1 -1
- package/lib/server.js +7 -6
- package/lib/server.js.map +1 -1
- package/lib/sql/cursor.d.ts +2 -2
- package/lib/sql/cursor.d.ts.map +1 -1
- package/lib/sql/cursor.js +2 -2
- package/lib/sql/cursor.js.map +1 -1
- package/lib/sql/printer.d.ts +3 -3
- package/lib/sql/printer.d.ts.map +1 -1
- package/lib/sql/printer.js +17 -15
- package/lib/sql/printer.js.map +1 -1
- package/lib/sql/query.d.ts +5 -5
- package/lib/sql/query.d.ts.map +1 -1
- package/lib/sql/query.js.map +1 -1
- package/lib/sql/util.d.ts +2 -2
- package/lib/sql/util.d.ts.map +1 -1
- package/lib/sql/util.js.map +1 -1
- package/lib/test/basic.test.js +471 -229
- package/lib/test/basic.test.js.map +1 -1
- package/lib/test/isNull.test.js +87 -36
- package/lib/test/isNull.test.js.map +1 -1
- package/lib/test/limits.test.js +206 -94
- package/lib/test/limits.test.js.map +1 -1
- package/lib/test/lookup.test.js +184 -81
- package/lib/test/lookup.test.js.map +1 -1
- package/lib/test/setup.js +2 -2
- package/lib/test/setup.js.map +1 -1
- package/lib/test/where.test.js +216 -99
- package/lib/test/where.test.js.map +1 -1
- package/package.json +5 -3
- package/src/context.ts +2 -2
- package/src/db.ts +4 -2
- package/src/dialect/common.ts +49 -0
- package/src/dialect/index.ts +20 -0
- package/src/{opencrud → dialect/opencrud}/orderBy.ts +4 -21
- package/src/{opencrud → dialect/opencrud}/schema.ts +30 -55
- package/src/{opencrud → dialect/opencrud}/tree.ts +6 -7
- package/src/{opencrud → dialect/opencrud}/where.ts +4 -16
- package/src/dialect/thegraph/locale.ts +284 -0
- package/src/dialect/thegraph/orderBy.ts +75 -0
- package/src/dialect/thegraph/schema.ts +484 -0
- package/src/dialect/thegraph/tree.ts +162 -0
- package/src/dialect/thegraph/where.ts +184 -0
- package/src/ir/args.ts +2 -0
- package/src/main.ts +3 -3
- package/src/model.schema.ts +37 -4
- package/src/model.ts +2 -0
- package/src/server.ts +21 -19
- package/src/sql/cursor.ts +4 -4
- package/src/sql/printer.ts +22 -18
- package/src/sql/query.ts +5 -5
- package/src/sql/util.ts +2 -2
- package/src/test/basic.test.ts +570 -282
- package/src/test/isNull.test.ts +95 -38
- package/src/test/limits.test.ts +212 -91
- package/src/test/lookup.test.ts +190 -83
- package/src/test/setup.ts +2 -2
- package/src/test/where.test.ts +235 -108
- package/lib/dialect.d.ts +0 -2
- package/lib/dialect.d.ts.map +0 -1
- package/lib/dialect.js +0 -3
- package/lib/dialect.js.map +0 -1
- package/lib/opencrud/orderBy.d.ts.map +0 -1
- package/lib/opencrud/orderBy.js.map +0 -1
- package/lib/opencrud/schema.d.ts.map +0 -1
- package/lib/opencrud/schema.js.map +0 -1
- package/lib/opencrud/tree.d.ts.map +0 -1
- package/lib/opencrud/tree.js.map +0 -1
- package/lib/opencrud/where.d.ts.map +0 -1
- package/lib/opencrud/where.js.map +0 -1
- package/src/dialect.ts +0 -2
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import {def, unexpectedCase} from '@subsquid/util-internal'
|
|
2
|
+
import {toCamelCase} from '@subsquid/util-naming'
|
|
3
|
+
import assert from 'assert'
|
|
4
|
+
import {
|
|
5
|
+
GraphQLBoolean,
|
|
6
|
+
GraphQLEnumType,
|
|
7
|
+
GraphQLFieldConfig,
|
|
8
|
+
GraphQLFloat,
|
|
9
|
+
GraphQLInputObjectType,
|
|
10
|
+
GraphQLInputType,
|
|
11
|
+
GraphQLInt,
|
|
12
|
+
GraphQLInterfaceType,
|
|
13
|
+
GraphQLList,
|
|
14
|
+
GraphQLNonNull,
|
|
15
|
+
GraphQLObjectType,
|
|
16
|
+
GraphQLOutputType,
|
|
17
|
+
GraphQLResolveInfo,
|
|
18
|
+
GraphQLScalarType,
|
|
19
|
+
GraphQLSchema,
|
|
20
|
+
GraphQLString,
|
|
21
|
+
GraphQLUnionType,
|
|
22
|
+
} from 'graphql'
|
|
23
|
+
import {
|
|
24
|
+
GraphQLEnumValueConfigMap,
|
|
25
|
+
GraphQLFieldConfigArgumentMap,
|
|
26
|
+
GraphQLFieldConfigMap,
|
|
27
|
+
GraphQLInputFieldConfigMap,
|
|
28
|
+
} from 'graphql/type/definition'
|
|
29
|
+
import {Context} from '../../context'
|
|
30
|
+
import {getListSize, getObjectSize} from '../../limit.size'
|
|
31
|
+
import {Entity, Interface, JsonObject, Model, Prop} from '../../model'
|
|
32
|
+
import {getEntity, getObject, getUniversalProperties} from '../../model.tools'
|
|
33
|
+
import {customScalars} from '../../scalars'
|
|
34
|
+
import {EntityByIdQuery, ListQuery} from '../../sql/query'
|
|
35
|
+
import {Limit} from '../../util/limit'
|
|
36
|
+
import {getResolveTree} from '../../util/resolve-tree'
|
|
37
|
+
import {identity} from '../../util/util'
|
|
38
|
+
import {getOrderByList, ORDER_DIRECTIONS} from './orderBy'
|
|
39
|
+
import {parseAnyTree, parseObjectTree, parseSqlArguments} from './tree'
|
|
40
|
+
import {GqlFieldMap, SchemaOptions} from '../common'
|
|
41
|
+
import {toPlural} from './locale'
|
|
42
|
+
|
|
43
|
+
export class SchemaBuilder {
|
|
44
|
+
private model: Model
|
|
45
|
+
private types = new Map<string, GraphQLOutputType>()
|
|
46
|
+
private where = new Map<string, GraphQLInputType>()
|
|
47
|
+
private orderBy = new Map<string, GraphQLInputType>()
|
|
48
|
+
|
|
49
|
+
constructor(private options: SchemaOptions) {
|
|
50
|
+
this.model = options.model
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private get(name: string): GraphQLOutputType
|
|
54
|
+
private get<T extends GraphQLOutputType>(name: string, kind: Type<T>): T
|
|
55
|
+
private get(name: string, kind?: Type<any>): GraphQLOutputType {
|
|
56
|
+
switch (name) {
|
|
57
|
+
case 'ID':
|
|
58
|
+
case 'String':
|
|
59
|
+
return GraphQLString
|
|
60
|
+
case 'Int':
|
|
61
|
+
return GraphQLInt
|
|
62
|
+
case 'Boolean':
|
|
63
|
+
return GraphQLBoolean
|
|
64
|
+
case 'Float':
|
|
65
|
+
return GraphQLFloat
|
|
66
|
+
case 'DateTime':
|
|
67
|
+
return customScalars.DateTime
|
|
68
|
+
case 'BigInt':
|
|
69
|
+
return customScalars.BigInt
|
|
70
|
+
case 'BigDecimal':
|
|
71
|
+
return customScalars.BigDecimal
|
|
72
|
+
case 'Bytes':
|
|
73
|
+
return customScalars.Bytes
|
|
74
|
+
case 'JSON':
|
|
75
|
+
return customScalars.JSON
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let type = this.types.get(name)
|
|
79
|
+
if (type == null) {
|
|
80
|
+
type = this.buildType(name)
|
|
81
|
+
this.types.set(name, type)
|
|
82
|
+
}
|
|
83
|
+
if (kind) {
|
|
84
|
+
assert(type instanceof kind)
|
|
85
|
+
}
|
|
86
|
+
return type
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private buildType(name: string): GraphQLOutputType {
|
|
90
|
+
const item = this.model[name]
|
|
91
|
+
switch (item.kind) {
|
|
92
|
+
case 'entity':
|
|
93
|
+
case 'object':
|
|
94
|
+
return new GraphQLObjectType({
|
|
95
|
+
name,
|
|
96
|
+
description: item.description,
|
|
97
|
+
interfaces: () => item.interfaces?.map((name) => this.get(name, GraphQLInterfaceType)),
|
|
98
|
+
fields: () => this.buildObjectFields(item),
|
|
99
|
+
})
|
|
100
|
+
case 'interface':
|
|
101
|
+
return new GraphQLInterfaceType({
|
|
102
|
+
name,
|
|
103
|
+
description: item.description,
|
|
104
|
+
fields: () => this.buildObjectFields(item),
|
|
105
|
+
resolveType: item.queryable ? (value: any) => value._isTypeOf : undefined,
|
|
106
|
+
})
|
|
107
|
+
case 'enum':
|
|
108
|
+
return new GraphQLEnumType({
|
|
109
|
+
name,
|
|
110
|
+
description: item.description,
|
|
111
|
+
values: Object.keys(item.values).reduce((values, variant) => {
|
|
112
|
+
values[variant] = {}
|
|
113
|
+
return values
|
|
114
|
+
}, {} as GraphQLEnumValueConfigMap),
|
|
115
|
+
})
|
|
116
|
+
case 'union':
|
|
117
|
+
return new GraphQLUnionType({
|
|
118
|
+
name,
|
|
119
|
+
description: item.description,
|
|
120
|
+
types: () => item.variants.map((variant) => this.get(variant, GraphQLObjectType)),
|
|
121
|
+
resolveType(value: any) {
|
|
122
|
+
return value.isTypeOf
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
default:
|
|
126
|
+
throw unexpectedCase()
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private buildObjectFields(object: Entity | JsonObject | Interface): GraphQLFieldConfigMap<any, any> {
|
|
131
|
+
let fields: GraphQLFieldConfigMap<any, any> = {}
|
|
132
|
+
for (let key in object.properties) {
|
|
133
|
+
let prop = object.properties[key]
|
|
134
|
+
let field: GraphQLFieldConfig<any, any> = {
|
|
135
|
+
description: prop.description,
|
|
136
|
+
type: this.getPropType(prop),
|
|
137
|
+
}
|
|
138
|
+
if (prop.type.kind == 'list-lookup') {
|
|
139
|
+
field.args = this.listArguments(prop.type.entity)
|
|
140
|
+
}
|
|
141
|
+
if (object.kind == 'entity' || object.kind == 'object') {
|
|
142
|
+
switch (prop.type.kind) {
|
|
143
|
+
case 'object':
|
|
144
|
+
case 'union':
|
|
145
|
+
case 'fk':
|
|
146
|
+
case 'lookup':
|
|
147
|
+
case 'list-lookup':
|
|
148
|
+
field.resolve = (source, args, context, info) => source[info.path.key]
|
|
149
|
+
break
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
fields[key] = field
|
|
153
|
+
}
|
|
154
|
+
return fields
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private getPropType(prop: Prop): GraphQLOutputType {
|
|
158
|
+
let type: GraphQLOutputType
|
|
159
|
+
switch (prop.type.kind) {
|
|
160
|
+
case 'list':
|
|
161
|
+
type = new GraphQLList(this.getPropType(prop.type.item))
|
|
162
|
+
break
|
|
163
|
+
case 'fk':
|
|
164
|
+
type = this.get(prop.type.entity)
|
|
165
|
+
break
|
|
166
|
+
case 'lookup':
|
|
167
|
+
return this.get(prop.type.entity)
|
|
168
|
+
case 'list-lookup':
|
|
169
|
+
return new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(this.get(prop.type.entity))))
|
|
170
|
+
default:
|
|
171
|
+
type = this.get(prop.type.name)
|
|
172
|
+
}
|
|
173
|
+
if (!prop.nullable) {
|
|
174
|
+
type = new GraphQLNonNull(type)
|
|
175
|
+
}
|
|
176
|
+
return type
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private listArguments(typeName: string): GraphQLFieldConfigArgumentMap {
|
|
180
|
+
return {
|
|
181
|
+
where: {type: this.getWhere(typeName)},
|
|
182
|
+
orderBy: {type: this.getOrderBy(typeName)},
|
|
183
|
+
orderDirection: {type: this.getOrderDirection()},
|
|
184
|
+
skip: {type: GraphQLInt},
|
|
185
|
+
first: {type: GraphQLInt},
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private getWhere(typeName: string): GraphQLInputType {
|
|
190
|
+
let where = this.where.get(typeName)
|
|
191
|
+
if (where) return where
|
|
192
|
+
|
|
193
|
+
let object = this.model[typeName]
|
|
194
|
+
let properties = getUniversalProperties(this.model, typeName)
|
|
195
|
+
|
|
196
|
+
where = new GraphQLInputObjectType({
|
|
197
|
+
name: `${typeName}_filter`,
|
|
198
|
+
fields: () => {
|
|
199
|
+
let fields: GraphQLInputFieldConfigMap = {}
|
|
200
|
+
|
|
201
|
+
for (let key in properties) {
|
|
202
|
+
this.buildPropWhereFilters(key, properties[key], fields)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (object.kind == 'entity' || object.kind == 'interface') {
|
|
206
|
+
let whereList = new GraphQLList(new GraphQLNonNull(this.getWhere(typeName)))
|
|
207
|
+
fields['and'] = {
|
|
208
|
+
type: whereList,
|
|
209
|
+
}
|
|
210
|
+
fields['or'] = {
|
|
211
|
+
type: whereList,
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return fields
|
|
216
|
+
},
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
this.where.set(typeName, where)
|
|
220
|
+
return where
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private buildPropWhereFilters(key: string, prop: Prop, fields: GraphQLInputFieldConfigMap): void {
|
|
224
|
+
switch (prop.type.kind) {
|
|
225
|
+
case 'scalar': {
|
|
226
|
+
let type = this.get(prop.type.name, GraphQLScalarType)
|
|
227
|
+
let listType = new GraphQLList(new GraphQLNonNull(type))
|
|
228
|
+
|
|
229
|
+
fields[`${key}_is_null`] = {type: GraphQLBoolean}
|
|
230
|
+
fields[`${key}`] = {type}
|
|
231
|
+
fields[`${key}_not`] = {type}
|
|
232
|
+
|
|
233
|
+
switch (prop.type.name) {
|
|
234
|
+
case 'ID':
|
|
235
|
+
case 'String':
|
|
236
|
+
case 'Int':
|
|
237
|
+
case 'Float':
|
|
238
|
+
case 'DateTime':
|
|
239
|
+
case 'BigInt':
|
|
240
|
+
case 'BigDecimal':
|
|
241
|
+
fields[`${key}_gt`] = {type}
|
|
242
|
+
fields[`${key}_gte`] = {type}
|
|
243
|
+
fields[`${key}_lt`] = {type}
|
|
244
|
+
fields[`${key}_lte`] = {type}
|
|
245
|
+
fields[`${key}_in`] = {type: listType}
|
|
246
|
+
fields[`${key}_not_in`] = {type: listType}
|
|
247
|
+
break
|
|
248
|
+
case 'JSON':
|
|
249
|
+
fields[`${key}_json_contains`] = {type}
|
|
250
|
+
fields[`${key}_json_has_key`] = {type}
|
|
251
|
+
break
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (prop.type.name == 'ID' || prop.type.name == 'String') {
|
|
255
|
+
fields[`${key}_contains`] = {type}
|
|
256
|
+
fields[`${key}_not_contains`] = {type}
|
|
257
|
+
fields[`${key}_contains_nocase`] = {type}
|
|
258
|
+
fields[`${key}_not_contains_nocase`] = {type}
|
|
259
|
+
fields[`${key}_starts_with`] = {type}
|
|
260
|
+
fields[`${key}_starts_with_nocase`] = {type}
|
|
261
|
+
fields[`${key}_not_starts_with`] = {type}
|
|
262
|
+
fields[`${key}_not_starts_with_nocase`] = {type}
|
|
263
|
+
fields[`${key}_ends_with`] = {type}
|
|
264
|
+
fields[`${key}_ends_with_nocase`] = {type}
|
|
265
|
+
fields[`${key}_not_ends_with`] = {type}
|
|
266
|
+
fields[`${key}_not_ends_with_nocase`] = {type}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
break
|
|
270
|
+
}
|
|
271
|
+
case 'enum': {
|
|
272
|
+
let type = this.get(prop.type.name, GraphQLEnumType)
|
|
273
|
+
let listType = new GraphQLList(new GraphQLNonNull(type))
|
|
274
|
+
fields[`${key}_is_null`] = {type: GraphQLBoolean}
|
|
275
|
+
fields[`${key}`] = {type}
|
|
276
|
+
fields[`${key}_not`] = {type}
|
|
277
|
+
fields[`${key}_in`] = {type: listType}
|
|
278
|
+
fields[`${key}_not_in`] = {type: listType}
|
|
279
|
+
break
|
|
280
|
+
}
|
|
281
|
+
case 'list':
|
|
282
|
+
fields[`${key}_isNull`] = {type: GraphQLBoolean}
|
|
283
|
+
if (prop.type.item.type.kind == 'scalar' || prop.type.item.type.kind == 'enum') {
|
|
284
|
+
let item = this.getPropType(prop.type.item)
|
|
285
|
+
let list = new GraphQLList(item)
|
|
286
|
+
fields[`${key}_contains_all`] = {type: list}
|
|
287
|
+
fields[`${key}_contains_any`] = {type: list}
|
|
288
|
+
fields[`${key}_contains_none`] = {type: list}
|
|
289
|
+
}
|
|
290
|
+
break
|
|
291
|
+
case 'object':
|
|
292
|
+
fields[`${key}_is_null`] = {type: GraphQLBoolean}
|
|
293
|
+
if (this.hasFilters(getObject(this.model, prop.type.name))) {
|
|
294
|
+
fields[`${key}_`] = {type: this.getWhere(prop.type.name)}
|
|
295
|
+
}
|
|
296
|
+
break
|
|
297
|
+
case 'union':
|
|
298
|
+
fields[`${key}_is_null`] = {type: GraphQLBoolean}
|
|
299
|
+
fields[key] = {type: this.getWhere(prop.type.name)}
|
|
300
|
+
break
|
|
301
|
+
case 'fk':
|
|
302
|
+
case 'lookup':
|
|
303
|
+
fields[`${key}_is_null`] = {type: GraphQLBoolean}
|
|
304
|
+
fields[`${key}_`] = {type: this.getWhere(prop.type.entity)}
|
|
305
|
+
break
|
|
306
|
+
case 'list-lookup': {
|
|
307
|
+
let where = this.getWhere(prop.type.entity)
|
|
308
|
+
fields[`${key}_every`] = {type: where}
|
|
309
|
+
fields[`${key}_some`] = {type: where}
|
|
310
|
+
fields[`${key}_none`] = {type: where}
|
|
311
|
+
break
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private hasFilters(obj: JsonObject): boolean {
|
|
317
|
+
for (let key in obj.properties) {
|
|
318
|
+
let propType = obj.properties[key].type
|
|
319
|
+
switch (propType.kind) {
|
|
320
|
+
case 'scalar':
|
|
321
|
+
case 'enum':
|
|
322
|
+
case 'union':
|
|
323
|
+
return true
|
|
324
|
+
case 'object': {
|
|
325
|
+
let ref = getObject(this.model, propType.name)
|
|
326
|
+
if (ref !== obj && this.hasFilters(ref)) {
|
|
327
|
+
return true
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return false
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private getOrderBy(typeName: string): GraphQLInputType {
|
|
336
|
+
let orderBy = this.orderBy.get(typeName)
|
|
337
|
+
if (orderBy) return orderBy
|
|
338
|
+
|
|
339
|
+
let values: GraphQLEnumValueConfigMap = {}
|
|
340
|
+
for (let variant of getOrderByList(this.model, typeName)) {
|
|
341
|
+
values[variant] = {}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
orderBy = new GraphQLEnumType({
|
|
345
|
+
name: `${typeName}_orderBy`,
|
|
346
|
+
values,
|
|
347
|
+
})
|
|
348
|
+
this.orderBy.set(typeName, orderBy)
|
|
349
|
+
return orderBy
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
@def
|
|
353
|
+
private getOrderDirection(): GraphQLInputType {
|
|
354
|
+
let values: GraphQLEnumValueConfigMap = {}
|
|
355
|
+
for (let variant of Object.keys(ORDER_DIRECTIONS)) {
|
|
356
|
+
values[variant] = {}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return new GraphQLEnumType({
|
|
360
|
+
name: `OrderDirection`,
|
|
361
|
+
values,
|
|
362
|
+
})
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
@def
|
|
366
|
+
build(): GraphQLSchema {
|
|
367
|
+
let query: GqlFieldMap = {}
|
|
368
|
+
let subscription: GqlFieldMap = {}
|
|
369
|
+
|
|
370
|
+
for (let name in this.model) {
|
|
371
|
+
let item = this.model[name]
|
|
372
|
+
switch (item.kind) {
|
|
373
|
+
case 'entity':
|
|
374
|
+
this.installEntityQuery(name, query, subscription)
|
|
375
|
+
this.installListQuery(name, query, subscription)
|
|
376
|
+
break
|
|
377
|
+
case 'interface':
|
|
378
|
+
if (item.queryable) {
|
|
379
|
+
this.installListQuery(name, query, subscription)
|
|
380
|
+
}
|
|
381
|
+
break
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return new GraphQLSchema({
|
|
386
|
+
query: new GraphQLObjectType({
|
|
387
|
+
name: 'Query',
|
|
388
|
+
fields: query,
|
|
389
|
+
}),
|
|
390
|
+
subscription: this.options.subscriptions
|
|
391
|
+
? new GraphQLObjectType({
|
|
392
|
+
name: 'Subscription',
|
|
393
|
+
fields: subscription,
|
|
394
|
+
})
|
|
395
|
+
: undefined,
|
|
396
|
+
})
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private installListQuery(typeName: string, query: GqlFieldMap, subscription: GqlFieldMap): void {
|
|
400
|
+
let model = this.model
|
|
401
|
+
|
|
402
|
+
let entity = model[typeName]
|
|
403
|
+
let queryName = (entity.kind === 'entity' && entity.listQueryName) || this.normalizeQueryName(typeName).plural
|
|
404
|
+
let outputType = new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(this.get(typeName))))
|
|
405
|
+
let argsType = this.listArguments(typeName)
|
|
406
|
+
|
|
407
|
+
function createQuery(context: Context, info: GraphQLResolveInfo, limit?: Limit) {
|
|
408
|
+
let tree = getResolveTree(info)
|
|
409
|
+
let args = parseSqlArguments(model, typeName, tree.args)
|
|
410
|
+
let fields = parseAnyTree(model, typeName, info.schema, tree)
|
|
411
|
+
limit?.check(() => getListSize(model, typeName, fields, args.limit, args.where) + 1)
|
|
412
|
+
return new ListQuery(model, context.openreader.dbType, typeName, fields, args)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
query[queryName] = {
|
|
416
|
+
type: outputType,
|
|
417
|
+
args: argsType,
|
|
418
|
+
resolve(source, args, context, info) {
|
|
419
|
+
let q = createQuery(context, info, context.openreader.responseSizeLimit)
|
|
420
|
+
return context.openreader.executeQuery(q)
|
|
421
|
+
},
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
subscription[queryName] = {
|
|
425
|
+
type: outputType,
|
|
426
|
+
args: argsType,
|
|
427
|
+
resolve: identity,
|
|
428
|
+
subscribe(source, args, context, info) {
|
|
429
|
+
let q = createQuery(context, info, context.openreader.subscriptionResponseSizeLimit)
|
|
430
|
+
return context.openreader.subscription(q)
|
|
431
|
+
},
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
private installEntityQuery(entityName: string, query: GqlFieldMap, subscription: GqlFieldMap): void {
|
|
436
|
+
let model = this.model
|
|
437
|
+
|
|
438
|
+
let entity = model[entityName]
|
|
439
|
+
let queryName = (entity.kind === 'entity' && entity.queryName) || this.normalizeQueryName(entityName).singular
|
|
440
|
+
let argsType = {
|
|
441
|
+
id: {type: new GraphQLNonNull(GraphQLString)},
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function createQuery(context: Context, info: GraphQLResolveInfo, limit?: Limit) {
|
|
445
|
+
let tree = getResolveTree(info)
|
|
446
|
+
let fields = parseObjectTree(model, entityName, info.schema, tree)
|
|
447
|
+
limit?.check(() => getObjectSize(model, fields) + 1)
|
|
448
|
+
return new EntityByIdQuery(model, context.openreader.dbType, entityName, fields, tree.args.id as string)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
query[queryName] = {
|
|
452
|
+
type: this.get(entityName),
|
|
453
|
+
args: argsType,
|
|
454
|
+
async resolve(source, args, context, info) {
|
|
455
|
+
let q = createQuery(context, info, context.openreader.responseSizeLimit)
|
|
456
|
+
return context.openreader.executeQuery(q)
|
|
457
|
+
},
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
subscription[queryName] = {
|
|
461
|
+
type: this.get(entityName),
|
|
462
|
+
args: argsType,
|
|
463
|
+
resolve: identity,
|
|
464
|
+
subscribe(source, args, context, info) {
|
|
465
|
+
let q = createQuery(context, info, context.openreader.subscriptionResponseSizeLimit)
|
|
466
|
+
return context.openreader.subscription(q)
|
|
467
|
+
},
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private normalizeQueryName(typeName: string) {
|
|
472
|
+
let singular = toCamelCase(typeName)
|
|
473
|
+
let plural = toPlural(singular)
|
|
474
|
+
if (singular === plural) {
|
|
475
|
+
plural += '_collection'
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return {singular, plural}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
interface Type<T> {
|
|
483
|
+
new (...args: any[]): T
|
|
484
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {unexpectedCase} from '@subsquid/util-internal'
|
|
2
|
+
import assert from 'assert'
|
|
3
|
+
import {GraphQLSchema} from 'graphql'
|
|
4
|
+
import {ResolveTree} from 'graphql-parse-resolve-info'
|
|
5
|
+
import {SqlArguments} from '../../ir/args'
|
|
6
|
+
import {AnyFields, FieldRequest, FieldsByEntity, OpaqueRequest} from '../../ir/fields'
|
|
7
|
+
import {Model} from '../../model'
|
|
8
|
+
import {getQueryableEntities} from '../../model.tools'
|
|
9
|
+
import {simplifyResolveTree} from '../../util/resolve-tree'
|
|
10
|
+
import {parseWhere} from './where'
|
|
11
|
+
import {parseOrderBy} from './orderBy'
|
|
12
|
+
|
|
13
|
+
export function parseObjectTree(
|
|
14
|
+
model: Model,
|
|
15
|
+
typeName: string,
|
|
16
|
+
schema: GraphQLSchema,
|
|
17
|
+
tree: ResolveTree
|
|
18
|
+
): FieldRequest[] {
|
|
19
|
+
let requests: FieldRequest[] = []
|
|
20
|
+
let requestedScalars: Record<string, true> = {}
|
|
21
|
+
let object = model[typeName]
|
|
22
|
+
assert(object.kind == 'entity' || object.kind == 'object')
|
|
23
|
+
|
|
24
|
+
let fields = simplifyResolveTree(schema, tree, typeName).fields
|
|
25
|
+
for (let alias in fields) {
|
|
26
|
+
let f = fields[alias]
|
|
27
|
+
let prop = object.properties[f.name]
|
|
28
|
+
switch (prop.type.kind) {
|
|
29
|
+
case 'scalar':
|
|
30
|
+
case 'enum':
|
|
31
|
+
case 'list':
|
|
32
|
+
if (requestedScalars[f.name] == null) {
|
|
33
|
+
requestedScalars[f.name] = true
|
|
34
|
+
requests.push({
|
|
35
|
+
field: f.name,
|
|
36
|
+
aliases: [f.name],
|
|
37
|
+
kind: prop.type.kind,
|
|
38
|
+
type: prop.type,
|
|
39
|
+
prop,
|
|
40
|
+
index: 0,
|
|
41
|
+
} as OpaqueRequest)
|
|
42
|
+
}
|
|
43
|
+
break
|
|
44
|
+
case 'object':
|
|
45
|
+
requests.push({
|
|
46
|
+
field: f.name,
|
|
47
|
+
aliases: [f.alias],
|
|
48
|
+
kind: prop.type.kind,
|
|
49
|
+
type: prop.type,
|
|
50
|
+
prop,
|
|
51
|
+
index: 0,
|
|
52
|
+
children: parseObjectTree(model, prop.type.name, schema, f),
|
|
53
|
+
})
|
|
54
|
+
break
|
|
55
|
+
case 'union': {
|
|
56
|
+
let union = model[prop.type.name]
|
|
57
|
+
assert(union.kind == 'union')
|
|
58
|
+
let children: FieldRequest[] = []
|
|
59
|
+
for (let variant of union.variants) {
|
|
60
|
+
for (let req of parseObjectTree(model, variant, schema, f)) {
|
|
61
|
+
req.ifType = variant
|
|
62
|
+
children.push(req)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
requests.push({
|
|
66
|
+
field: f.name,
|
|
67
|
+
aliases: [f.alias],
|
|
68
|
+
kind: prop.type.kind,
|
|
69
|
+
type: prop.type,
|
|
70
|
+
prop,
|
|
71
|
+
index: 0,
|
|
72
|
+
children,
|
|
73
|
+
})
|
|
74
|
+
break
|
|
75
|
+
}
|
|
76
|
+
case 'fk':
|
|
77
|
+
requests.push({
|
|
78
|
+
field: f.name,
|
|
79
|
+
aliases: [f.alias],
|
|
80
|
+
kind: prop.type.kind,
|
|
81
|
+
type: prop.type,
|
|
82
|
+
prop,
|
|
83
|
+
index: 0,
|
|
84
|
+
children: parseObjectTree(model, prop.type.entity, schema, f),
|
|
85
|
+
})
|
|
86
|
+
break
|
|
87
|
+
case 'lookup':
|
|
88
|
+
requests.push({
|
|
89
|
+
field: f.name,
|
|
90
|
+
aliases: [f.alias],
|
|
91
|
+
kind: prop.type.kind,
|
|
92
|
+
type: prop.type,
|
|
93
|
+
prop,
|
|
94
|
+
index: 0,
|
|
95
|
+
children: parseObjectTree(model, prop.type.entity, schema, f),
|
|
96
|
+
})
|
|
97
|
+
break
|
|
98
|
+
case 'list-lookup':
|
|
99
|
+
requests.push({
|
|
100
|
+
field: f.name,
|
|
101
|
+
aliases: [f.alias],
|
|
102
|
+
kind: prop.type.kind,
|
|
103
|
+
type: prop.type,
|
|
104
|
+
prop,
|
|
105
|
+
index: 0,
|
|
106
|
+
args: parseSqlArguments(model, prop.type.entity, f.args),
|
|
107
|
+
children: parseObjectTree(model, prop.type.entity, schema, f),
|
|
108
|
+
})
|
|
109
|
+
break
|
|
110
|
+
default:
|
|
111
|
+
throw unexpectedCase()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return requests
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function parseSqlArguments(model: Model, typeName: string, gqlArgs: any): SqlArguments {
|
|
119
|
+
let args: SqlArguments = {}
|
|
120
|
+
|
|
121
|
+
let where = parseWhere(gqlArgs.where)
|
|
122
|
+
if (where) {
|
|
123
|
+
args.where = where
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (gqlArgs.orderBy) {
|
|
127
|
+
args.orderBy = parseOrderBy(model, typeName, {orderBy: gqlArgs.orderBy, direction: gqlArgs.orderDirection})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (gqlArgs.skip) {
|
|
131
|
+
assert(typeof gqlArgs.skip == 'number')
|
|
132
|
+
args.offset = gqlArgs.skip
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (gqlArgs.first != null) {
|
|
136
|
+
assert(typeof gqlArgs.first == 'number')
|
|
137
|
+
args.limit = gqlArgs.first
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return args
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function parseQueryableTree(
|
|
144
|
+
model: Model,
|
|
145
|
+
queryableName: string,
|
|
146
|
+
schema: GraphQLSchema,
|
|
147
|
+
tree: ResolveTree
|
|
148
|
+
): FieldsByEntity {
|
|
149
|
+
let fields: FieldsByEntity = {}
|
|
150
|
+
for (let entity of getQueryableEntities(model, queryableName)) {
|
|
151
|
+
fields[entity] = parseObjectTree(model, entity, schema, tree)
|
|
152
|
+
}
|
|
153
|
+
return fields
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function parseAnyTree(model: Model, typeName: string, schema: GraphQLSchema, tree: ResolveTree): AnyFields {
|
|
157
|
+
if (model[typeName].kind == 'interface') {
|
|
158
|
+
return parseQueryableTree(model, typeName, schema, tree)
|
|
159
|
+
} else {
|
|
160
|
+
return parseObjectTree(model, typeName, schema, tree)
|
|
161
|
+
}
|
|
162
|
+
}
|