@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.
- package/lib/ir/args.d.ts +1 -1
- package/lib/ir/args.d.ts.map +1 -1
- package/lib/ir/connection.d.ts +3 -4
- package/lib/ir/connection.d.ts.map +1 -1
- package/lib/ir/connection.js.map +1 -1
- package/lib/ir/fields.d.ts +6 -2
- package/lib/ir/fields.d.ts.map +1 -1
- package/lib/ir/fields.js +15 -0
- package/lib/ir/fields.js.map +1 -1
- package/lib/limit.size.d.ts +4 -4
- package/lib/limit.size.d.ts.map +1 -1
- package/lib/limit.size.js +51 -20
- package/lib/limit.size.js.map +1 -1
- package/lib/main.js +0 -9
- package/lib/main.js.map +1 -1
- package/lib/model.d.ts +3 -2
- package/lib/model.d.ts.map +1 -1
- package/lib/model.schema.d.ts +2 -2
- package/lib/model.schema.d.ts.map +1 -1
- package/lib/model.schema.js +8 -3
- package/lib/model.schema.js.map +1 -1
- package/lib/model.tools.d.ts +6 -1
- package/lib/model.tools.d.ts.map +1 -1
- package/lib/model.tools.js +111 -8
- package/lib/model.tools.js.map +1 -1
- package/lib/opencrud/orderBy.d.ts +2 -2
- package/lib/opencrud/orderBy.d.ts.map +1 -1
- package/lib/opencrud/orderBy.js +13 -17
- package/lib/opencrud/orderBy.js.map +1 -1
- package/lib/opencrud/schema.d.ts +4 -4
- package/lib/opencrud/schema.d.ts.map +1 -1
- package/lib/opencrud/schema.js +56 -62
- package/lib/opencrud/schema.js.map +1 -1
- package/lib/opencrud/tree.d.ts +9 -7
- package/lib/opencrud/tree.d.ts.map +1 -1
- package/lib/opencrud/tree.js +32 -14
- package/lib/opencrud/tree.js.map +1 -1
- package/lib/scalars/BigDecimal.d.ts +3 -0
- package/lib/scalars/BigDecimal.d.ts.map +1 -0
- package/lib/scalars/BigDecimal.js +38 -0
- package/lib/scalars/BigDecimal.js.map +1 -0
- package/lib/scalars/index.d.ts +1 -0
- package/lib/scalars/index.d.ts.map +1 -1
- package/lib/scalars/index.js +2 -0
- package/lib/scalars/index.js.map +1 -1
- package/lib/server.d.ts +4 -2
- package/lib/server.d.ts.map +1 -1
- package/lib/server.js +4 -2
- package/lib/server.js.map +1 -1
- package/lib/sql/cursor.d.ts.map +1 -1
- package/lib/sql/cursor.js +4 -2
- package/lib/sql/cursor.js.map +1 -1
- package/lib/sql/mapping.d.ts +3 -1
- package/lib/sql/mapping.d.ts.map +1 -1
- package/lib/sql/mapping.js +16 -1
- package/lib/sql/mapping.js.map +1 -1
- package/lib/sql/printer.d.ts +29 -11
- package/lib/sql/printer.d.ts.map +1 -1
- package/lib/sql/printer.js +112 -11
- package/lib/sql/printer.js.map +1 -1
- package/lib/sql/query.d.ts +11 -11
- package/lib/sql/query.d.ts.map +1 -1
- package/lib/sql/query.js +41 -19
- package/lib/sql/query.js.map +1 -1
- package/lib/test/queryable.test.d.ts +2 -0
- package/lib/test/queryable.test.d.ts.map +1 -0
- package/lib/test/queryable.test.js +255 -0
- package/lib/test/queryable.test.js.map +1 -0
- package/lib/test/scalars.test.js +75 -1
- package/lib/test/scalars.test.js.map +1 -1
- package/lib/util/big-decimal.d.ts +5 -0
- package/lib/util/big-decimal.d.ts.map +1 -0
- package/lib/util/big-decimal.js +15 -0
- package/lib/util/big-decimal.js.map +1 -0
- package/package.json +10 -2
- package/src/ir/args.ts +1 -1
- package/src/ir/connection.ts +3 -4
- package/src/ir/fields.ts +22 -2
- package/src/limit.size.ts +55 -30
- package/src/main.ts +0 -11
- package/src/model.schema.ts +16 -9
- package/src/model.tools.ts +121 -8
- package/src/model.ts +3 -2
- package/src/opencrud/orderBy.ts +13 -17
- package/src/opencrud/schema.ts +81 -84
- package/src/opencrud/tree.ts +55 -26
- package/src/scalars/BigDecimal.ts +37 -0
- package/src/scalars/index.ts +2 -0
- package/src/server.ts +10 -7
- package/src/sql/cursor.ts +4 -2
- package/src/sql/mapping.ts +18 -1
- package/src/sql/printer.ts +142 -22
- package/src/sql/query.ts +50 -30
- package/src/test/queryable.test.ts +258 -0
- package/src/test/scalars.test.ts +78 -1
- package/src/util/big-decimal.ts +15 -0
package/src/opencrud/schema.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {def, unexpectedCase} from
|
|
2
|
-
import {toCamelCase, toPlural} from
|
|
3
|
-
import {UserInputError} from
|
|
4
|
-
import assert from
|
|
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
|
|
23
|
+
} from 'graphql'
|
|
24
24
|
import {
|
|
25
25
|
GraphQLEnumValueConfigMap,
|
|
26
26
|
GraphQLFieldConfigArgumentMap,
|
|
27
27
|
GraphQLFieldConfigMap,
|
|
28
28
|
GraphQLInputFieldConfigMap
|
|
29
|
-
} from
|
|
30
|
-
import {Context} from
|
|
31
|
-
import {decodeRelayConnectionCursor, RelayConnectionRequest} from
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
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
|
|
40
|
-
import {ensureArray, identity} from
|
|
41
|
-
import {getOrderByMapping, parseOrderBy} from
|
|
42
|
-
import {
|
|
43
|
-
import {parseWhere} from
|
|
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.
|
|
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.
|
|
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
|
|
198
|
+
private listArguments(typeName: string): GraphQLFieldConfigArgumentMap {
|
|
195
199
|
return {
|
|
196
|
-
where: {type: this.getWhere(
|
|
197
|
-
orderBy: {type: this.getOrderBy(
|
|
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(
|
|
204
|
-
let where = this.where.get(
|
|
207
|
+
private getWhere(typeName: string): GraphQLInputType {
|
|
208
|
+
let where = this.where.get(typeName)
|
|
205
209
|
if (where) return where
|
|
206
210
|
|
|
207
|
-
|
|
208
|
-
|
|
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: `${
|
|
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(
|
|
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
|
-
|
|
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(
|
|
359
|
-
let orderBy = this.orderBy.get(
|
|
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,
|
|
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: `${
|
|
361
|
+
name: `${typeName}OrderByInput`,
|
|
371
362
|
values
|
|
372
363
|
})
|
|
373
364
|
)
|
|
374
365
|
)
|
|
375
|
-
this.orderBy.set(
|
|
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.
|
|
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
|
|
405
|
+
private installListQuery(typeName: string, query: GqlFieldMap, subscription: GqlFieldMap): void {
|
|
409
406
|
let model = this.model
|
|
410
|
-
let queryName = toPlural(toCamelCase(
|
|
411
|
-
let outputType = new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(this.get(
|
|
412
|
-
let argsType = this.
|
|
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
|
|
417
|
-
let
|
|
418
|
-
limit?.check(() =>
|
|
419
|
-
return new
|
|
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
|
-
|
|
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 =
|
|
458
|
-
limit?.check(() =>
|
|
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 =
|
|
500
|
-
context.openreader.responseSizeLimit?.check(() =>
|
|
501
|
-
let query = new
|
|
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(
|
|
512
|
+
private installRelayConnection(typeName: string, query: GqlFieldMap): void {
|
|
516
513
|
let model = this.model
|
|
517
|
-
let outputType = toPlural(
|
|
518
|
-
let edgeType = `${
|
|
514
|
+
let outputType = toPlural(typeName) + 'Connection'
|
|
515
|
+
let edgeType = `${typeName}Edge`
|
|
519
516
|
|
|
520
|
-
query[`${toPlural(toCamelCase(
|
|
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(
|
|
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(
|
|
535
|
+
orderBy: {type: new GraphQLNonNull(this.getOrderBy(typeName))},
|
|
539
536
|
after: {type: GraphQLString},
|
|
540
537
|
first: {type: GraphQLInt},
|
|
541
|
-
where: {type: this.getWhere(
|
|
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,
|
|
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 =
|
|
578
|
+
req.edgeNode = parseAnyTree(model, typeName, info.schema, nodeTree)
|
|
582
579
|
}
|
|
583
580
|
}
|
|
584
581
|
|
|
585
|
-
context.openreader.responseSizeLimit?.check(() =>
|
|
582
|
+
context.openreader.responseSizeLimit?.check(() => getConnectionSize(model, typeName, req) + 1)
|
|
586
583
|
|
|
587
|
-
let result = await context.openreader.executeQuery(new
|
|
584
|
+
let result = await context.openreader.executeQuery(new ConnectionQuery(
|
|
588
585
|
model,
|
|
589
586
|
context.openreader.dialect,
|
|
590
|
-
|
|
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
|
|
592
|
+
result.totalCount = await context.openreader.executeQuery(new CountQuery(
|
|
596
593
|
model,
|
|
597
594
|
context.openreader.dialect,
|
|
598
|
-
|
|
595
|
+
typeName,
|
|
599
596
|
req.where
|
|
600
597
|
))
|
|
601
598
|
}
|
package/src/opencrud/tree.ts
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
import {unexpectedCase} from
|
|
2
|
-
import assert from
|
|
3
|
-
import {GraphQLSchema} from
|
|
4
|
-
import {ResolveTree} from
|
|
5
|
-
import {
|
|
6
|
-
import {FieldRequest, OpaqueRequest} from
|
|
7
|
-
import {Model} from
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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[
|
|
24
|
+
let object = model[typeName]
|
|
24
25
|
assert(object.kind == "entity" || object.kind == "object")
|
|
25
26
|
|
|
26
|
-
let fields = simplifyResolveTree(schema, tree,
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
109
|
-
children:
|
|
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
|
|
122
|
-
let args:
|
|
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,
|
|
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
|
+
}
|
package/src/scalars/index.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
286
|
+
prop.type.entity,
|
|
285
287
|
{on: 'id', rhs: this.string(field)}
|
|
286
288
|
)
|
|
287
289
|
default:
|