@subsquid/openreader 4.6.0 → 5.0.0-beta.79fbfe

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.
Files changed (131) hide show
  1. package/lib/context.d.ts +2 -2
  2. package/lib/context.d.ts.map +1 -1
  3. package/lib/db.d.ts +3 -3
  4. package/lib/db.d.ts.map +1 -1
  5. package/lib/db.js +2 -2
  6. package/lib/db.js.map +1 -1
  7. package/lib/dialect/common.d.ts +19 -0
  8. package/lib/dialect/common.d.ts.map +1 -0
  9. package/lib/dialect/common.js +42 -0
  10. package/lib/dialect/common.js.map +1 -0
  11. package/lib/dialect/index.d.ts +6 -0
  12. package/lib/dialect/index.d.ts.map +1 -0
  13. package/lib/dialect/index.js +49 -0
  14. package/lib/dialect/index.js.map +1 -0
  15. package/lib/{opencrud → dialect/opencrud}/orderBy.d.ts +2 -3
  16. package/lib/dialect/opencrud/orderBy.d.ts.map +1 -0
  17. package/lib/{opencrud → dialect/opencrud}/orderBy.js +4 -21
  18. package/lib/dialect/opencrud/orderBy.js.map +1 -0
  19. package/lib/{opencrud → dialect/opencrud}/schema.d.ts +2 -6
  20. package/lib/dialect/opencrud/schema.d.ts.map +1 -0
  21. package/lib/{opencrud → dialect/opencrud}/schema.js +21 -34
  22. package/lib/dialect/opencrud/schema.js.map +1 -0
  23. package/lib/{opencrud → dialect/opencrud}/tree.d.ts +3 -3
  24. package/lib/dialect/opencrud/tree.d.ts.map +1 -0
  25. package/lib/{opencrud → dialect/opencrud}/tree.js +3 -3
  26. package/lib/dialect/opencrud/tree.js.map +1 -0
  27. package/lib/{opencrud → dialect/opencrud}/where.d.ts +1 -1
  28. package/lib/dialect/opencrud/where.d.ts.map +1 -0
  29. package/lib/{opencrud → dialect/opencrud}/where.js +4 -13
  30. package/lib/dialect/opencrud/where.js.map +1 -0
  31. package/lib/dialect/thegraph/locale.d.ts +2 -0
  32. package/lib/dialect/thegraph/locale.d.ts.map +1 -0
  33. package/lib/dialect/thegraph/locale.js +49 -0
  34. package/lib/dialect/thegraph/locale.js.map +1 -0
  35. package/lib/dialect/thegraph/orderBy.d.ts +11 -0
  36. package/lib/dialect/thegraph/orderBy.d.ts.map +1 -0
  37. package/lib/dialect/thegraph/orderBy.js +71 -0
  38. package/lib/dialect/thegraph/orderBy.js.map +1 -0
  39. package/lib/dialect/thegraph/schema.d.ts +25 -0
  40. package/lib/dialect/thegraph/schema.d.ts.map +1 -0
  41. package/lib/dialect/thegraph/schema.js +429 -0
  42. package/lib/dialect/thegraph/schema.js.map +1 -0
  43. package/lib/dialect/thegraph/tree.d.ts +10 -0
  44. package/lib/dialect/thegraph/tree.d.ts.map +1 -0
  45. package/lib/dialect/thegraph/tree.js +148 -0
  46. package/lib/dialect/thegraph/tree.js.map +1 -0
  47. package/lib/dialect/thegraph/where.d.ts +9 -0
  48. package/lib/dialect/thegraph/where.d.ts.map +1 -0
  49. package/lib/dialect/thegraph/where.js +188 -0
  50. package/lib/dialect/thegraph/where.js.map +1 -0
  51. package/lib/ir/args.d.ts +1 -1
  52. package/lib/ir/args.d.ts.map +1 -1
  53. package/lib/main.js +1 -1
  54. package/lib/main.js.map +1 -1
  55. package/lib/model.d.ts +2 -0
  56. package/lib/model.d.ts.map +1 -1
  57. package/lib/model.schema.d.ts.map +1 -1
  58. package/lib/model.schema.js +31 -2
  59. package/lib/model.schema.js.map +1 -1
  60. package/lib/server.d.ts +3 -1
  61. package/lib/server.d.ts.map +1 -1
  62. package/lib/server.js +7 -6
  63. package/lib/server.js.map +1 -1
  64. package/lib/sql/cursor.d.ts +2 -2
  65. package/lib/sql/cursor.d.ts.map +1 -1
  66. package/lib/sql/cursor.js +2 -2
  67. package/lib/sql/cursor.js.map +1 -1
  68. package/lib/sql/printer.d.ts +3 -3
  69. package/lib/sql/printer.d.ts.map +1 -1
  70. package/lib/sql/printer.js +17 -15
  71. package/lib/sql/printer.js.map +1 -1
  72. package/lib/sql/query.d.ts +5 -5
  73. package/lib/sql/query.d.ts.map +1 -1
  74. package/lib/sql/query.js.map +1 -1
  75. package/lib/sql/util.d.ts +2 -2
  76. package/lib/sql/util.d.ts.map +1 -1
  77. package/lib/sql/util.js.map +1 -1
  78. package/lib/test/basic.test.js +471 -229
  79. package/lib/test/basic.test.js.map +1 -1
  80. package/lib/test/isNull.test.js +87 -36
  81. package/lib/test/isNull.test.js.map +1 -1
  82. package/lib/test/limits.test.js +206 -94
  83. package/lib/test/limits.test.js.map +1 -1
  84. package/lib/test/lookup.test.js +184 -81
  85. package/lib/test/lookup.test.js.map +1 -1
  86. package/lib/test/setup.js +2 -2
  87. package/lib/test/setup.js.map +1 -1
  88. package/lib/test/where.test.js +216 -99
  89. package/lib/test/where.test.js.map +1 -1
  90. package/package.json +5 -3
  91. package/src/context.ts +2 -2
  92. package/src/db.ts +4 -2
  93. package/src/dialect/common.ts +49 -0
  94. package/src/dialect/index.ts +20 -0
  95. package/src/{opencrud → dialect/opencrud}/orderBy.ts +4 -21
  96. package/src/{opencrud → dialect/opencrud}/schema.ts +30 -55
  97. package/src/{opencrud → dialect/opencrud}/tree.ts +6 -7
  98. package/src/{opencrud → dialect/opencrud}/where.ts +4 -16
  99. package/src/dialect/thegraph/locale.ts +284 -0
  100. package/src/dialect/thegraph/orderBy.ts +75 -0
  101. package/src/dialect/thegraph/schema.ts +484 -0
  102. package/src/dialect/thegraph/tree.ts +162 -0
  103. package/src/dialect/thegraph/where.ts +184 -0
  104. package/src/ir/args.ts +2 -0
  105. package/src/main.ts +3 -3
  106. package/src/model.schema.ts +37 -4
  107. package/src/model.ts +2 -0
  108. package/src/server.ts +21 -19
  109. package/src/sql/cursor.ts +4 -4
  110. package/src/sql/printer.ts +22 -18
  111. package/src/sql/query.ts +5 -5
  112. package/src/sql/util.ts +2 -2
  113. package/src/test/basic.test.ts +570 -282
  114. package/src/test/isNull.test.ts +95 -38
  115. package/src/test/limits.test.ts +212 -91
  116. package/src/test/lookup.test.ts +190 -83
  117. package/src/test/setup.ts +2 -2
  118. package/src/test/where.test.ts +235 -108
  119. package/lib/dialect.d.ts +0 -2
  120. package/lib/dialect.d.ts.map +0 -1
  121. package/lib/dialect.js +0 -3
  122. package/lib/dialect.js.map +0 -1
  123. package/lib/opencrud/orderBy.d.ts.map +0 -1
  124. package/lib/opencrud/orderBy.js.map +0 -1
  125. package/lib/opencrud/schema.d.ts.map +0 -1
  126. package/lib/opencrud/schema.js.map +0 -1
  127. package/lib/opencrud/tree.d.ts.map +0 -1
  128. package/lib/opencrud/tree.js.map +0 -1
  129. package/lib/opencrud/where.d.ts.map +0 -1
  130. package/lib/opencrud/where.js.map +0 -1
  131. 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
+ }