@subsquid/openreader 2.1.0 → 3.1.0

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 (96) hide show
  1. package/lib/ir/args.d.ts +1 -1
  2. package/lib/ir/args.d.ts.map +1 -1
  3. package/lib/ir/connection.d.ts +3 -4
  4. package/lib/ir/connection.d.ts.map +1 -1
  5. package/lib/ir/connection.js.map +1 -1
  6. package/lib/ir/fields.d.ts +6 -2
  7. package/lib/ir/fields.d.ts.map +1 -1
  8. package/lib/ir/fields.js +15 -0
  9. package/lib/ir/fields.js.map +1 -1
  10. package/lib/limit.size.d.ts +4 -4
  11. package/lib/limit.size.d.ts.map +1 -1
  12. package/lib/limit.size.js +51 -20
  13. package/lib/limit.size.js.map +1 -1
  14. package/lib/main.js +0 -9
  15. package/lib/main.js.map +1 -1
  16. package/lib/model.d.ts +3 -2
  17. package/lib/model.d.ts.map +1 -1
  18. package/lib/model.schema.d.ts +2 -2
  19. package/lib/model.schema.d.ts.map +1 -1
  20. package/lib/model.schema.js +8 -3
  21. package/lib/model.schema.js.map +1 -1
  22. package/lib/model.tools.d.ts +6 -1
  23. package/lib/model.tools.d.ts.map +1 -1
  24. package/lib/model.tools.js +111 -8
  25. package/lib/model.tools.js.map +1 -1
  26. package/lib/opencrud/orderBy.d.ts +2 -2
  27. package/lib/opencrud/orderBy.d.ts.map +1 -1
  28. package/lib/opencrud/orderBy.js +13 -17
  29. package/lib/opencrud/orderBy.js.map +1 -1
  30. package/lib/opencrud/schema.d.ts +4 -4
  31. package/lib/opencrud/schema.d.ts.map +1 -1
  32. package/lib/opencrud/schema.js +56 -62
  33. package/lib/opencrud/schema.js.map +1 -1
  34. package/lib/opencrud/tree.d.ts +9 -7
  35. package/lib/opencrud/tree.d.ts.map +1 -1
  36. package/lib/opencrud/tree.js +32 -14
  37. package/lib/opencrud/tree.js.map +1 -1
  38. package/lib/scalars/BigDecimal.d.ts +3 -0
  39. package/lib/scalars/BigDecimal.d.ts.map +1 -0
  40. package/lib/scalars/BigDecimal.js +38 -0
  41. package/lib/scalars/BigDecimal.js.map +1 -0
  42. package/lib/scalars/index.d.ts +1 -0
  43. package/lib/scalars/index.d.ts.map +1 -1
  44. package/lib/scalars/index.js +2 -0
  45. package/lib/scalars/index.js.map +1 -1
  46. package/lib/server.d.ts +4 -2
  47. package/lib/server.d.ts.map +1 -1
  48. package/lib/server.js +4 -2
  49. package/lib/server.js.map +1 -1
  50. package/lib/sql/cursor.d.ts.map +1 -1
  51. package/lib/sql/cursor.js +4 -2
  52. package/lib/sql/cursor.js.map +1 -1
  53. package/lib/sql/mapping.d.ts +3 -1
  54. package/lib/sql/mapping.d.ts.map +1 -1
  55. package/lib/sql/mapping.js +16 -1
  56. package/lib/sql/mapping.js.map +1 -1
  57. package/lib/sql/printer.d.ts +29 -11
  58. package/lib/sql/printer.d.ts.map +1 -1
  59. package/lib/sql/printer.js +112 -11
  60. package/lib/sql/printer.js.map +1 -1
  61. package/lib/sql/query.d.ts +11 -11
  62. package/lib/sql/query.d.ts.map +1 -1
  63. package/lib/sql/query.js +41 -19
  64. package/lib/sql/query.js.map +1 -1
  65. package/lib/test/queryable.test.d.ts +2 -0
  66. package/lib/test/queryable.test.d.ts.map +1 -0
  67. package/lib/test/queryable.test.js +255 -0
  68. package/lib/test/queryable.test.js.map +1 -0
  69. package/lib/test/scalars.test.js +75 -1
  70. package/lib/test/scalars.test.js.map +1 -1
  71. package/lib/util/big-decimal.d.ts +5 -0
  72. package/lib/util/big-decimal.d.ts.map +1 -0
  73. package/lib/util/big-decimal.js +15 -0
  74. package/lib/util/big-decimal.js.map +1 -0
  75. package/package.json +10 -2
  76. package/src/ir/args.ts +1 -1
  77. package/src/ir/connection.ts +3 -4
  78. package/src/ir/fields.ts +22 -2
  79. package/src/limit.size.ts +55 -30
  80. package/src/main.ts +0 -11
  81. package/src/model.schema.ts +16 -9
  82. package/src/model.tools.ts +121 -8
  83. package/src/model.ts +3 -2
  84. package/src/opencrud/orderBy.ts +13 -17
  85. package/src/opencrud/schema.ts +81 -84
  86. package/src/opencrud/tree.ts +55 -26
  87. package/src/scalars/BigDecimal.ts +37 -0
  88. package/src/scalars/index.ts +2 -0
  89. package/src/server.ts +10 -7
  90. package/src/sql/cursor.ts +4 -2
  91. package/src/sql/mapping.ts +18 -1
  92. package/src/sql/printer.ts +142 -22
  93. package/src/sql/query.ts +50 -30
  94. package/src/test/queryable.test.ts +258 -0
  95. package/src/test/scalars.test.ts +78 -1
  96. package/src/util/big-decimal.ts +15 -0
@@ -1,7 +1,7 @@
1
- import {def, unexpectedCase} from "@subsquid/util-internal"
2
- import {toCamelCase, toPlural} from "@subsquid/util-naming"
3
- import {UserInputError} from "apollo-server-core"
4
- import assert from "assert"
1
+ import {def, unexpectedCase} from '@subsquid/util-internal'
2
+ import {toCamelCase, toPlural} from '@subsquid/util-naming'
3
+ import {UserInputError} from 'apollo-server-core'
4
+ import assert from 'assert'
5
5
  import {
6
6
  GraphQLBoolean,
7
7
  GraphQLEnumType,
@@ -20,27 +20,28 @@ import {
20
20
  GraphQLSchema,
21
21
  GraphQLString,
22
22
  GraphQLUnionType
23
- } from "graphql"
23
+ } from 'graphql'
24
24
  import {
25
25
  GraphQLEnumValueConfigMap,
26
26
  GraphQLFieldConfigArgumentMap,
27
27
  GraphQLFieldConfigMap,
28
28
  GraphQLInputFieldConfigMap
29
- } from "graphql/type/definition"
30
- import {Context} from "../context"
31
- import {decodeRelayConnectionCursor, RelayConnectionRequest} from "../ir/connection"
32
- import {getEntityListSize, getRelaySize, getSize} from '../limit.size'
33
- import {Entity, Interface, JsonObject, Model, Prop} from "../model"
34
- import {getObject, getUnionProps} from "../model.tools"
35
- import {customScalars} from "../scalars"
36
- import {EntityByIdQuery, EntityConnectionQuery, EntityCountQuery, EntityListQuery, Query} from "../sql/query"
37
- import {Subscription} from "../subscription"
29
+ } from 'graphql/type/definition'
30
+ import {Context} from '../context'
31
+ import {decodeRelayConnectionCursor, RelayConnectionRequest} from '../ir/connection'
32
+ import {AnyFields} from '../ir/fields'
33
+ import {getConnectionSize, getListSize, getObjectSize} from '../limit.size'
34
+ import {Entity, Interface, JsonObject, Model, Prop} from '../model'
35
+ import {getObject, getUniversalProperties} from '../model.tools'
36
+ import {customScalars} from '../scalars'
37
+ import {ConnectionQuery, CountQuery, EntityByIdQuery, ListQuery, Query} from '../sql/query'
38
+ import {Subscription} from '../subscription'
38
39
  import {Limit} from '../util/limit'
39
- import {getResolveTree, getTreeRequest, hasTreeRequest, simplifyResolveTree} from "../util/resolve-tree"
40
- import {ensureArray, identity} from "../util/util"
41
- import {getOrderByMapping, parseOrderBy} from "./orderBy"
42
- import {parseEntityListArguments, parseResolveTree} from "./tree"
43
- import {parseWhere} from "./where"
40
+ import {getResolveTree, getTreeRequest, hasTreeRequest, simplifyResolveTree} from '../util/resolve-tree'
41
+ import {ensureArray, identity} from '../util/util'
42
+ import {getOrderByMapping, parseOrderBy} from './orderBy'
43
+ import {parseAnyTree, parseObjectTree, parseSqlArguments} from './tree'
44
+ import {parseWhere} from './where'
44
45
 
45
46
 
46
47
  type GqlFieldMap = GraphQLFieldConfigMap<unknown, Context>
@@ -79,6 +80,8 @@ export class SchemaBuilder {
79
80
  return customScalars.DateTime
80
81
  case 'BigInt':
81
82
  return customScalars.BigInt
83
+ case 'BigDecimal':
84
+ return customScalars.BigDecimal
82
85
  case 'Bytes':
83
86
  return customScalars.Bytes
84
87
  case 'JSON':
@@ -111,7 +114,8 @@ export class SchemaBuilder {
111
114
  return new GraphQLInterfaceType({
112
115
  name,
113
116
  description: item.description,
114
- fields: () => this.buildObjectFields(item)
117
+ fields: () => this.buildObjectFields(item),
118
+ resolveType: item.queryable ? (value: any) => value._isTypeOf : undefined
115
119
  })
116
120
  case "enum":
117
121
  return new GraphQLEnumType({
@@ -145,7 +149,7 @@ export class SchemaBuilder {
145
149
  type: this.getPropType(prop)
146
150
  }
147
151
  if (prop.type.kind == 'list-lookup') {
148
- field.args = this.entityListArguments(prop.type.entity)
152
+ field.args = this.listArguments(prop.type.entity)
149
153
  }
150
154
  if (object.kind == 'entity' || object.kind == 'object') {
151
155
  switch(prop.type.kind) {
@@ -170,7 +174,7 @@ export class SchemaBuilder {
170
174
  type = new GraphQLList(this.getPropType(prop.type.item))
171
175
  break
172
176
  case "fk":
173
- type = this.get(prop.type.foreignEntity)
177
+ type = this.get(prop.type.entity)
174
178
  break
175
179
  case "lookup":
176
180
  return this.get(prop.type.entity)
@@ -191,46 +195,33 @@ export class SchemaBuilder {
191
195
  return type
192
196
  }
193
197
 
194
- private entityListArguments(entityName: string): GraphQLFieldConfigArgumentMap {
198
+ private listArguments(typeName: string): GraphQLFieldConfigArgumentMap {
195
199
  return {
196
- where: {type: this.getWhere(entityName)},
197
- orderBy: {type: this.getOrderBy(entityName)},
200
+ where: {type: this.getWhere(typeName)},
201
+ orderBy: {type: this.getOrderBy(typeName)},
198
202
  offset: {type: GraphQLInt},
199
203
  limit: {type: GraphQLInt}
200
204
  }
201
205
  }
202
206
 
203
- private getWhere(name: string): GraphQLInputType {
204
- let where = this.where.get(name)
207
+ private getWhere(typeName: string): GraphQLInputType {
208
+ let where = this.where.get(typeName)
205
209
  if (where) return where
206
210
 
207
- const object = this.model[name]
208
- switch(object.kind) {
209
- case "entity":
210
- case "object":
211
- case "union":
212
- break
213
- default:
214
- throw unexpectedCase(object.kind)
215
- }
211
+ let object = this.model[typeName]
212
+ let properties = getUniversalProperties(this.model, typeName)
216
213
 
217
214
  where = new GraphQLInputObjectType({
218
- name: `${name}WhereInput`,
215
+ name: `${typeName}WhereInput`,
219
216
  fields: () => {
220
217
  let fields: GraphQLInputFieldConfigMap = {}
221
- let properties
222
- if (object.kind == 'union') {
223
- properties = getUnionProps(this.model, name).properties
224
- } else {
225
- properties = object.properties
226
- }
227
218
 
228
219
  for (let key in properties) {
229
220
  this.buildPropWhereFilters(key, properties[key], fields)
230
221
  }
231
222
 
232
- if (object.kind == 'entity') {
233
- let whereList = new GraphQLList(new GraphQLNonNull(this.getWhere(name)))
223
+ if (object.kind == 'entity' || object.kind == 'interface') {
224
+ let whereList = new GraphQLList(new GraphQLNonNull(this.getWhere(typeName)))
234
225
  fields['AND'] = {
235
226
  type: whereList
236
227
  }
@@ -242,7 +233,8 @@ export class SchemaBuilder {
242
233
  return fields
243
234
  }
244
235
  })
245
- this.where.set(name, where)
236
+
237
+ this.where.set(typeName, where)
246
238
  return where
247
239
  }
248
240
 
@@ -263,6 +255,7 @@ export class SchemaBuilder {
263
255
  case 'Float':
264
256
  case 'DateTime':
265
257
  case 'BigInt':
258
+ case 'BigDecimal':
266
259
  fields[`${key}_gt`] = {type}
267
260
  fields[`${key}_gte`] = {type}
268
261
  fields[`${key}_lt`] = {type}
@@ -320,10 +313,8 @@ export class SchemaBuilder {
320
313
  fields[key] = {type: this.getWhere(prop.type.name)}
321
314
  break
322
315
  case "fk":
323
- fields[`${key}_isNull`] = {type: GraphQLBoolean}
324
- fields[key] = {type: this.getWhere(prop.type.foreignEntity)}
325
- break
326
316
  case "lookup":
317
+ fields[`${key}_isNull`] = {type: GraphQLBoolean}
327
318
  fields[key] = {type: this.getWhere(prop.type.entity)}
328
319
  break
329
320
  case "list-lookup": {
@@ -355,24 +346,24 @@ export class SchemaBuilder {
355
346
  return false
356
347
  }
357
348
 
358
- private getOrderBy(entityName: string): GraphQLInputType {
359
- let orderBy = this.orderBy.get(entityName)
349
+ private getOrderBy(typeName: string): GraphQLInputType {
350
+ let orderBy = this.orderBy.get(typeName)
360
351
  if (orderBy) return orderBy
361
352
 
362
353
  let values: GraphQLEnumValueConfigMap = {}
363
- for (let variant of getOrderByMapping(this.model, entityName).keys()) {
354
+ for (let variant of getOrderByMapping(this.model, typeName).keys()) {
364
355
  values[variant] = {}
365
356
  }
366
357
 
367
358
  orderBy = new GraphQLList(
368
359
  new GraphQLNonNull(
369
360
  new GraphQLEnumType({
370
- name: `${entityName}OrderByInput`,
361
+ name: `${typeName}OrderByInput`,
371
362
  values
372
363
  })
373
364
  )
374
365
  )
375
- this.orderBy.set(entityName, orderBy)
366
+ this.orderBy.set(typeName, orderBy)
376
367
  return orderBy
377
368
  }
378
369
 
@@ -385,11 +376,17 @@ export class SchemaBuilder {
385
376
  let item = this.model[name]
386
377
  switch(item.kind) {
387
378
  case "entity":
388
- this.installEntityList(name, query, subscription)
379
+ this.installListQuery(name, query, subscription)
389
380
  this.installEntityById(name, query, subscription)
390
381
  this.installEntityByUniqueInput(name, query)
391
382
  this.installRelayConnection(name, query)
392
383
  break
384
+ case 'interface':
385
+ if (item.queryable) {
386
+ this.installListQuery(name, query, subscription)
387
+ this.installRelayConnection(name, query)
388
+ }
389
+ break
393
390
  }
394
391
  }
395
392
 
@@ -405,21 +402,21 @@ export class SchemaBuilder {
405
402
  })
406
403
  }
407
404
 
408
- private installEntityList(entityName: string, query: GqlFieldMap, subscription: GqlFieldMap): void {
405
+ private installListQuery(typeName: string, query: GqlFieldMap, subscription: GqlFieldMap): void {
409
406
  let model = this.model
410
- let queryName = toPlural(toCamelCase(entityName))
411
- let outputType = new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(this.get(entityName))))
412
- let argsType = this.entityListArguments(entityName)
407
+ let queryName = toPlural(toCamelCase(typeName))
408
+ let outputType = new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(this.get(typeName))))
409
+ let argsType = this.listArguments(typeName)
413
410
 
414
411
  function createQuery(context: Context, info: GraphQLResolveInfo, limit?: Limit) {
415
412
  let tree = getResolveTree(info)
416
- let fields = parseResolveTree(model, entityName, info.schema, tree)
417
- let args = parseEntityListArguments(model, entityName, tree.args)
418
- limit?.check(() => getEntityListSize(model, entityName, fields, args.limit, args.where) + 1)
419
- return new EntityListQuery(
413
+ let args = parseSqlArguments(model, typeName, tree.args)
414
+ let fields = parseAnyTree(model, typeName, info.schema, tree)
415
+ limit?.check(() => getListSize(model, typeName, fields, args.limit, args.where) + 1)
416
+ return new ListQuery(
420
417
  model,
421
418
  context.openreader.dialect,
422
- entityName,
419
+ typeName,
423
420
  fields,
424
421
  args
425
422
  )
@@ -454,8 +451,8 @@ export class SchemaBuilder {
454
451
 
455
452
  function createQuery(context: Context, info: GraphQLResolveInfo, limit?: Limit) {
456
453
  let tree = getResolveTree(info)
457
- let fields = parseResolveTree(model, entityName, info.schema, tree)
458
- limit?.check(() => getSize(model, fields) + 1)
454
+ let fields = parseObjectTree(model, entityName, info.schema, tree)
455
+ limit?.check(() => getObjectSize(model, fields) + 1)
459
456
  return new EntityByIdQuery(
460
457
  model,
461
458
  context.openreader.dialect,
@@ -496,9 +493,9 @@ export class SchemaBuilder {
496
493
  },
497
494
  async resolve(source, args, context, info) {
498
495
  let tree = getResolveTree(info)
499
- let fields = parseResolveTree(model, entityName, info.schema, tree)
500
- context.openreader.responseSizeLimit?.check(() => getSize(model, fields) + 1)
501
- let query = new EntityListQuery(
496
+ let fields = parseObjectTree(model, entityName, info.schema, tree)
497
+ context.openreader.responseSizeLimit?.check(() => getObjectSize(model, fields) + 1)
498
+ let query = new ListQuery(
502
499
  model,
503
500
  context.openreader.dialect,
504
501
  entityName,
@@ -512,12 +509,12 @@ export class SchemaBuilder {
512
509
  }
513
510
  }
514
511
 
515
- private installRelayConnection(entityName: string, query: GqlFieldMap): void {
512
+ private installRelayConnection(typeName: string, query: GqlFieldMap): void {
516
513
  let model = this.model
517
- let outputType = toPlural(entityName) + 'Connection'
518
- let edgeType = `${entityName}Edge`
514
+ let outputType = toPlural(typeName) + 'Connection'
515
+ let edgeType = `${typeName}Edge`
519
516
 
520
- query[`${toPlural(toCamelCase(entityName))}Connection`] = {
517
+ query[`${toPlural(toCamelCase(typeName))}Connection`] = {
521
518
  type: new GraphQLNonNull(new GraphQLObjectType({
522
519
  name: outputType,
523
520
  fields: {
@@ -525,7 +522,7 @@ export class SchemaBuilder {
525
522
  type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(new GraphQLObjectType({
526
523
  name: edgeType,
527
524
  fields: {
528
- node: {type: new GraphQLNonNull(this.get(entityName))},
525
+ node: {type: new GraphQLNonNull(this.get(typeName))},
529
526
  cursor: {type: new GraphQLNonNull(GraphQLString)}
530
527
  }
531
528
  }))))
@@ -535,10 +532,10 @@ export class SchemaBuilder {
535
532
  }
536
533
  })),
537
534
  args: {
538
- orderBy: {type: new GraphQLNonNull(this.getOrderBy(entityName))},
535
+ orderBy: {type: new GraphQLNonNull(this.getOrderBy(typeName))},
539
536
  after: {type: GraphQLString},
540
537
  first: {type: GraphQLInt},
541
- where: {type: this.getWhere(entityName)}
538
+ where: {type: this.getWhere(typeName)}
542
539
  },
543
540
  async resolve(source, args, context, info) {
544
541
  let orderByArg = ensureArray(args.orderBy)
@@ -546,8 +543,8 @@ export class SchemaBuilder {
546
543
  throw new UserInputError('orderBy argument is required for connection')
547
544
  }
548
545
 
549
- let req: RelayConnectionRequest = {
550
- orderBy: parseOrderBy(model, entityName, orderByArg),
546
+ let req: RelayConnectionRequest<AnyFields> = {
547
+ orderBy: parseOrderBy(model, typeName, orderByArg),
551
548
  where: parseWhere(args.where)
552
549
  }
553
550
 
@@ -578,24 +575,24 @@ export class SchemaBuilder {
578
575
  req.edgeCursor = hasTreeRequest(edgeFields, 'cursor')
579
576
  let nodeTree = getTreeRequest(edgeFields, 'node')
580
577
  if (nodeTree) {
581
- req.edgeNode = parseResolveTree(model, entityName, info.schema, nodeTree)
578
+ req.edgeNode = parseAnyTree(model, typeName, info.schema, nodeTree)
582
579
  }
583
580
  }
584
581
 
585
- context.openreader.responseSizeLimit?.check(() => getRelaySize(model, entityName, req) + 1)
582
+ context.openreader.responseSizeLimit?.check(() => getConnectionSize(model, typeName, req) + 1)
586
583
 
587
- let result = await context.openreader.executeQuery(new EntityConnectionQuery(
584
+ let result = await context.openreader.executeQuery(new ConnectionQuery(
588
585
  model,
589
586
  context.openreader.dialect,
590
- entityName,
587
+ typeName,
591
588
  req
592
589
  ))
593
590
 
594
591
  if (req.totalCount && result.totalCount == null) {
595
- result.totalCount = await context.openreader.executeQuery(new EntityCountQuery(
592
+ result.totalCount = await context.openreader.executeQuery(new CountQuery(
596
593
  model,
597
594
  context.openreader.dialect,
598
- entityName,
595
+ typeName,
599
596
  req.where
600
597
  ))
601
598
  }
@@ -1,29 +1,30 @@
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 {EntityListArguments} from "../ir/args"
6
- import {FieldRequest, OpaqueRequest} from "../ir/fields"
7
- import {Model} from "../model"
8
- import {simplifyResolveTree} from "../util/resolve-tree"
9
- import {ensureArray} from "../util/util"
10
- import {parseOrderBy} from "./orderBy"
11
- import {parseWhere} from "./where"
12
-
13
-
14
- export function parseResolveTree(
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 {ensureArray} from '../util/util'
11
+ import {parseOrderBy} from './orderBy'
12
+ import {parseWhere} from './where'
13
+
14
+
15
+ export function parseObjectTree(
15
16
  model: Model,
16
- objectName: string,
17
+ typeName: string,
17
18
  schema: GraphQLSchema,
18
19
  tree: ResolveTree
19
20
  ): FieldRequest[] {
20
21
 
21
22
  let requests: FieldRequest[] = []
22
23
  let requestedScalars: Record<string, true> = {}
23
- let object = model[objectName]
24
+ let object = model[typeName]
24
25
  assert(object.kind == "entity" || object.kind == "object")
25
26
 
26
- let fields = simplifyResolveTree(schema, tree, objectName).fields
27
+ let fields = simplifyResolveTree(schema, tree, typeName).fields
27
28
  for (let alias in fields) {
28
29
  let f = fields[alias]
29
30
  let prop = object.properties[f.name]
@@ -51,7 +52,7 @@ export function parseResolveTree(
51
52
  type: prop.type,
52
53
  prop,
53
54
  index: 0,
54
- children: parseResolveTree(model, prop.type.name, schema, f)
55
+ children: parseObjectTree(model, prop.type.name, schema, f)
55
56
  })
56
57
  break
57
58
  case "union": {
@@ -59,7 +60,7 @@ export function parseResolveTree(
59
60
  assert(union.kind == "union")
60
61
  let children: FieldRequest[] = []
61
62
  for (let variant of union.variants) {
62
- for (let req of parseResolveTree(model, variant, schema, f)) {
63
+ for (let req of parseObjectTree(model, variant, schema, f)) {
63
64
  req.ifType = variant
64
65
  children.push(req)
65
66
  }
@@ -83,7 +84,7 @@ export function parseResolveTree(
83
84
  type: prop.type,
84
85
  prop,
85
86
  index: 0,
86
- children: parseResolveTree(model, prop.type.foreignEntity, schema, f)
87
+ children: parseObjectTree(model, prop.type.entity, schema, f)
87
88
  })
88
89
  break
89
90
  case "lookup":
@@ -94,7 +95,7 @@ export function parseResolveTree(
94
95
  type: prop.type,
95
96
  prop,
96
97
  index: 0,
97
- children: parseResolveTree(model, prop.type.entity, schema, f)
98
+ children: parseObjectTree(model, prop.type.entity, schema, f)
98
99
  })
99
100
  break
100
101
  case "list-lookup":
@@ -105,8 +106,8 @@ export function parseResolveTree(
105
106
  type: prop.type,
106
107
  prop,
107
108
  index: 0,
108
- args: parseEntityListArguments(model, prop.type.entity, f.args),
109
- children: parseResolveTree(model, prop.type.entity, schema, f)
109
+ args: parseSqlArguments(model, prop.type.entity, f.args),
110
+ children: parseObjectTree(model, prop.type.entity, schema, f)
110
111
  })
111
112
  break
112
113
  default:
@@ -118,8 +119,8 @@ export function parseResolveTree(
118
119
  }
119
120
 
120
121
 
121
- export function parseEntityListArguments(model: Model, entityName: string, gqlArgs: any): EntityListArguments {
122
- let args: EntityListArguments = {}
122
+ export function parseSqlArguments(model: Model, typeName: string, gqlArgs: any): SqlArguments {
123
+ let args: SqlArguments = {}
123
124
 
124
125
  let where = parseWhere(gqlArgs.where)
125
126
  if (where) {
@@ -127,7 +128,7 @@ export function parseEntityListArguments(model: Model, entityName: string, gqlAr
127
128
  }
128
129
 
129
130
  if (gqlArgs.orderBy) {
130
- args.orderBy = parseOrderBy(model, entityName, ensureArray(gqlArgs.orderBy))
131
+ args.orderBy = parseOrderBy(model, typeName, ensureArray(gqlArgs.orderBy))
131
132
  }
132
133
 
133
134
  if (gqlArgs.offset) {
@@ -142,3 +143,31 @@ export function parseEntityListArguments(model: Model, entityName: string, gqlAr
142
143
 
143
144
  return args
144
145
  }
146
+
147
+
148
+ export function parseQueryableTree(
149
+ model: Model,
150
+ queryableName: string,
151
+ schema: GraphQLSchema,
152
+ tree: ResolveTree
153
+ ): FieldsByEntity {
154
+ let fields: FieldsByEntity = {}
155
+ for (let entity of getQueryableEntities(model, queryableName)) {
156
+ fields[entity] = parseObjectTree(model, entity, schema, tree)
157
+ }
158
+ return fields
159
+ }
160
+
161
+
162
+ export function parseAnyTree(
163
+ model: Model,
164
+ typeName: string,
165
+ schema: GraphQLSchema,
166
+ tree: ResolveTree
167
+ ): AnyFields {
168
+ if (model[typeName].kind == 'interface') {
169
+ return parseQueryableTree(model, typeName, schema, tree)
170
+ } else {
171
+ return parseObjectTree(model, typeName, schema, tree)
172
+ }
173
+ }
@@ -0,0 +1,37 @@
1
+ import type {BigDecimal, BigDecimalSource} from '@subsquid/big-decimal'
2
+ import {GraphQLScalarType} from 'graphql'
3
+ import {bigDecimal} from '../util/big-decimal'
4
+ import {invalidFormat} from '../util/util'
5
+
6
+
7
+ export const BigDecimalScalar = new GraphQLScalarType({
8
+ name: 'BigDecimal',
9
+ description: 'Big number decimal',
10
+ serialize(value: BigDecimalSource) {
11
+ return bigDecimal.BigDecimal(value).toString()
12
+ },
13
+ parseValue(value: string) {
14
+ if (!isDecimal(value)) throw invalidFormat('BigDecimal', value)
15
+ return bigDecimal.BigDecimal(value)
16
+ },
17
+ parseLiteral(ast) {
18
+ switch (ast.kind) {
19
+ case "StringValue":
20
+ if (isDecimal(ast.value)) {
21
+ return bigDecimal.BigDecimal(ast.value)
22
+ } else {
23
+ throw invalidFormat('BigDecimal', ast.value)
24
+ }
25
+ case "IntValue":
26
+ case "FloatValue":
27
+ return bigDecimal.BigDecimal(ast.value)
28
+ default:
29
+ return null
30
+ }
31
+ }
32
+ })
33
+
34
+
35
+ function isDecimal(s: string): boolean {
36
+ return /^[+\-]?\d+\.?(\d+)?(e[+\-]?\d+)?$/.test(s)
37
+ }
@@ -1,4 +1,5 @@
1
1
  import {BigIntScalar} from "./BigInt"
2
+ import {BigDecimalScalar} from "./BigDecimal"
2
3
  import {BytesScalar} from "./Bytes"
3
4
  import {DateTimeScalar} from "./DateTime"
4
5
  import {JSONScalar} from "./JSON"
@@ -6,6 +7,7 @@ import {JSONScalar} from "./JSON"
6
7
 
7
8
  export const customScalars = {
8
9
  BigInt: BigIntScalar,
10
+ BigDecimal: BigDecimalScalar,
9
11
  Bytes: BytesScalar,
10
12
  DateTime: DateTimeScalar,
11
13
  JSON: JSONScalar,
package/src/server.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type {Logger} from '@subsquid/logger'
1
+ import {Logger} from '@subsquid/logger'
2
2
  import {listen, ListeningServer} from '@subsquid/util-internal-http-server'
3
- import {PluginDefinition} from 'apollo-server-core'
3
+ import {KeyValueCache, PluginDefinition} from 'apollo-server-core'
4
4
  import {ApolloServer} from 'apollo-server-express'
5
5
  import express from 'express'
6
6
  import fs from 'fs'
@@ -33,10 +33,10 @@ export interface ServerOptions {
33
33
  subscriptions?: boolean
34
34
  subscriptionPollInterval?: number
35
35
  subscriptionConnection?: Pool
36
- subscriptionMaxResponseNodes?: number
36
+ subscriptionMaxResponseNodes?: number,
37
+ cache?: KeyValueCache
37
38
  }
38
39
 
39
-
40
40
  export async function serve(options: ServerOptions): Promise<ListeningServer> {
41
41
  let {connection, subscriptionConnection, subscriptionPollInterval, maxResponseNodes, subscriptionMaxResponseNodes} = options
42
42
  let dialect = options.dialect ?? 'postgres'
@@ -76,7 +76,8 @@ export async function serve(options: ServerOptions): Promise<ListeningServer> {
76
76
  log: options.log,
77
77
  graphiqlConsole: options.graphiqlConsole,
78
78
  maxRequestSizeBytes: options.maxRequestSizeBytes,
79
- maxRootFields: options.maxRootFields
79
+ maxRootFields: options.maxRootFields,
80
+ cache: options.cache,
80
81
  }), options.log)
81
82
  }
82
83
 
@@ -95,6 +96,7 @@ export interface ApolloOptions {
95
96
  log?: Logger
96
97
  maxRequestSizeBytes?: number
97
98
  maxRootFields?: number
99
+ cache?: KeyValueCache
98
100
  }
99
101
 
100
102
 
@@ -138,9 +140,11 @@ export async function runApollo(options: ApolloOptions): Promise<ListeningServer
138
140
  disposals.push(async () => wsServerCleanup.dispose())
139
141
  }
140
142
 
143
+
141
144
  let apollo = new ApolloServer({
142
145
  schema,
143
146
  context,
147
+ cache: options.cache,
144
148
  stopOnTerminationSignals: false,
145
149
  executor: execute && (async req => {
146
150
  return execute({
@@ -169,7 +173,7 @@ export async function runApollo(options: ApolloOptions): Promise<ListeningServer
169
173
  }
170
174
  }
171
175
  }
172
- }
176
+ },
173
177
  ]
174
178
  })
175
179
 
@@ -190,7 +194,6 @@ export async function runApollo(options: ApolloOptions): Promise<ListeningServer
190
194
  return listen(server, options.port)
191
195
  }
192
196
 
193
-
194
197
  export function addServerCleanup(disposals: Dispose[], server: Promise<ListeningServer>, log?: Logger): Promise<ListeningServer> {
195
198
  async function cleanup() {
196
199
  for (let i = disposals.length - 1; i >= 0; i--) {
package/src/sql/cursor.ts CHANGED
@@ -154,7 +154,7 @@ export class EntityCursor implements Cursor {
154
154
  case "fk":
155
155
  return new EntityCursor(
156
156
  this.ctx,
157
- prop.type.foreignEntity,
157
+ prop.type.entity,
158
158
  {on: 'id', rhs: this.native(field)}
159
159
  )
160
160
  case "lookup":
@@ -252,6 +252,8 @@ export class ObjectCursor implements Cursor {
252
252
  return `(${this.string(field)})::bool`
253
253
  case 'BigInt':
254
254
  return `(${this.string(field)})::numeric`
255
+ case 'BigDecimal':
256
+ return `(${this.string(field)})::numeric`
255
257
  case 'Bytes':
256
258
  return `decode(substr(${this.string(field)}, 3), 'hex')`
257
259
  case 'DateTime':
@@ -281,7 +283,7 @@ export class ObjectCursor implements Cursor {
281
283
  case "fk":
282
284
  return new EntityCursor(
283
285
  this.ctx,
284
- prop.type.foreignEntity,
286
+ prop.type.entity,
285
287
  {on: 'id', rhs: this.string(field)}
286
288
  )
287
289
  default: