@subsquid/openreader 1.0.2 → 2.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/bin/main.js +1 -1
- package/lib/context.d.ts +14 -0
- package/lib/context.d.ts.map +1 -0
- package/lib/context.js +3 -0
- package/lib/context.js.map +1 -0
- package/lib/db.d.ts +23 -0
- package/lib/db.d.ts.map +1 -0
- package/lib/db.js +57 -0
- package/lib/db.js.map +1 -0
- package/{dist → lib}/dialect.d.ts +0 -0
- package/{dist → lib}/dialect.d.ts.map +0 -0
- package/{dist → lib}/dialect.js +0 -0
- package/{dist → lib}/dialect.js.map +0 -0
- package/lib/ir/args.d.ts +47 -0
- package/lib/ir/args.d.ts.map +1 -0
- package/lib/ir/args.js +3 -0
- package/lib/ir/args.js.map +1 -0
- package/lib/ir/connection.d.ts +30 -0
- package/lib/ir/connection.d.ts.map +1 -0
- package/lib/ir/connection.js +17 -0
- package/lib/ir/connection.js.map +1 -0
- package/lib/ir/fields.d.ts +22 -0
- package/lib/ir/fields.d.ts.map +1 -0
- package/lib/ir/fields.js +3 -0
- package/lib/ir/fields.js.map +1 -0
- package/lib/limit.size.d.ts +8 -0
- package/lib/limit.size.d.ts.map +1 -0
- package/lib/limit.size.js +107 -0
- package/lib/limit.size.js.map +1 -0
- package/{dist → lib}/main.d.ts +0 -0
- package/{dist → lib}/main.d.ts.map +0 -0
- package/lib/main.js +59 -0
- package/lib/main.js.map +1 -0
- package/{dist → lib}/model.d.ts +11 -1
- package/lib/model.d.ts.map +1 -0
- package/{dist → lib}/model.js +0 -0
- package/{dist → lib}/model.js.map +0 -0
- package/{dist/gql/schema.d.ts → lib/model.schema.d.ts} +2 -2
- package/lib/model.schema.d.ts.map +1 -0
- package/{dist/gql/schema.js → lib/model.schema.js} +62 -8
- package/lib/model.schema.js.map +1 -0
- package/{dist → lib}/model.tools.d.ts +0 -0
- package/{dist → lib}/model.tools.d.ts.map +0 -0
- package/{dist → lib}/model.tools.js +0 -0
- package/{dist → lib}/model.tools.js.map +0 -0
- package/{dist → lib/opencrud}/orderBy.d.ts +2 -5
- package/lib/opencrud/orderBy.d.ts.map +1 -0
- package/{dist → lib/opencrud}/orderBy.js +1 -1
- package/lib/opencrud/orderBy.js.map +1 -0
- package/lib/opencrud/schema.d.ts +31 -0
- package/lib/opencrud/schema.d.ts.map +1 -0
- package/lib/opencrud/schema.js +527 -0
- package/lib/opencrud/schema.js.map +1 -0
- package/lib/opencrud/tree.d.ts +8 -0
- package/lib/opencrud/tree.d.ts.map +1 -0
- package/lib/opencrud/tree.js +131 -0
- package/lib/opencrud/tree.js.map +1 -0
- package/lib/opencrud/where.d.ts +7 -0
- package/lib/opencrud/where.d.ts.map +1 -0
- package/lib/opencrud/where.js +141 -0
- package/lib/opencrud/where.js.map +1 -0
- package/{dist/gql → lib}/scalars/BigInt.d.ts +0 -0
- package/lib/scalars/BigInt.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/BigInt.js +1 -1
- package/lib/scalars/BigInt.js.map +1 -0
- package/{dist/gql → lib}/scalars/Bytes.d.ts +0 -0
- package/lib/scalars/Bytes.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/Bytes.js +2 -2
- package/lib/scalars/Bytes.js.map +1 -0
- package/{dist/gql → lib}/scalars/DateTime.d.ts +0 -0
- package/lib/scalars/DateTime.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/DateTime.js +1 -1
- package/lib/scalars/DateTime.js.map +1 -0
- package/{dist/gql → lib}/scalars/JSON.d.ts +0 -0
- package/lib/scalars/JSON.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/JSON.js +0 -0
- package/lib/scalars/JSON.js.map +1 -0
- package/{dist/gql → lib}/scalars/index.d.ts +0 -0
- package/lib/scalars/index.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/index.js +0 -0
- package/lib/scalars/index.js.map +1 -0
- package/lib/server.d.ts +42 -0
- package/lib/server.d.ts.map +1 -0
- package/lib/server.js +171 -0
- package/lib/server.js.map +1 -0
- package/lib/sql/cursor.d.ts +52 -0
- package/lib/sql/cursor.d.ts.map +1 -0
- package/lib/sql/cursor.js +234 -0
- package/lib/sql/cursor.js.map +1 -0
- package/lib/sql/mapping.d.ts +4 -0
- package/lib/sql/mapping.d.ts.map +1 -0
- package/lib/sql/mapping.js +71 -0
- package/lib/sql/mapping.js.map +1 -0
- package/lib/sql/printer.d.ts +37 -0
- package/lib/sql/printer.d.ts.map +1 -0
- package/lib/sql/printer.js +311 -0
- package/lib/sql/printer.js.map +1 -0
- package/lib/sql/query.d.ts +46 -0
- package/lib/sql/query.d.ts.map +1 -0
- package/lib/sql/query.js +134 -0
- package/lib/sql/query.js.map +1 -0
- package/lib/sql/util.d.ts +30 -0
- package/lib/sql/util.d.ts.map +1 -0
- package/lib/sql/util.js +75 -0
- package/lib/sql/util.js.map +1 -0
- package/lib/subscription.d.ts +18 -0
- package/lib/subscription.d.ts.map +1 -0
- package/lib/subscription.js +47 -0
- package/lib/subscription.js.map +1 -0
- package/{dist → lib}/test/basic.test.d.ts +0 -0
- package/{dist → lib}/test/basic.test.d.ts.map +0 -0
- package/{dist → lib}/test/basic.test.js +0 -0
- package/{dist → lib}/test/basic.test.js.map +0 -0
- package/{dist → lib}/test/connection.test.d.ts +0 -0
- package/{dist → lib}/test/connection.test.d.ts.map +0 -0
- package/{dist → lib}/test/connection.test.js +0 -0
- package/{dist → lib}/test/connection.test.js.map +0 -0
- package/{dist → lib}/test/fts.test.d.ts +0 -0
- package/{dist → lib}/test/fts.test.d.ts.map +0 -0
- package/{dist → lib}/test/fts.test.js +1 -1
- package/lib/test/fts.test.js.map +1 -0
- package/{dist → lib}/test/isNull.test.d.ts +0 -0
- package/{dist → lib}/test/isNull.test.d.ts.map +0 -0
- package/{dist → lib}/test/isNull.test.js +0 -0
- package/{dist → lib}/test/isNull.test.js.map +0 -0
- package/lib/test/limits.test.d.ts +2 -0
- package/lib/test/limits.test.d.ts.map +1 -0
- package/lib/test/limits.test.js +159 -0
- package/lib/test/limits.test.js.map +1 -0
- package/{dist → lib}/test/lists.test.d.ts +0 -0
- package/{dist → lib}/test/lists.test.d.ts.map +0 -0
- package/{dist → lib}/test/lists.test.js +0 -0
- package/{dist → lib}/test/lists.test.js.map +0 -0
- package/{dist → lib}/test/lookup.test.d.ts +0 -0
- package/{dist → lib}/test/lookup.test.d.ts.map +0 -0
- package/{dist → lib}/test/lookup.test.js +0 -0
- package/{dist → lib}/test/lookup.test.js.map +0 -0
- package/{dist → lib}/test/regressions.test.d.ts +0 -0
- package/{dist → lib}/test/regressions.test.d.ts.map +0 -0
- package/{dist → lib}/test/regressions.test.js +0 -0
- package/{dist → lib}/test/regressions.test.js.map +0 -0
- package/{dist → lib}/test/scalars.test.d.ts +0 -0
- package/{dist → lib}/test/scalars.test.d.ts.map +0 -0
- package/{dist → lib}/test/scalars.test.js +0 -0
- package/{dist → lib}/test/scalars.test.js.map +0 -0
- package/lib/test/setup.d.ts +17 -0
- package/lib/test/setup.d.ts.map +1 -0
- package/{dist → lib}/test/setup.js +18 -13
- package/lib/test/setup.js.map +1 -0
- package/lib/test/subscription.test.d.ts +2 -0
- package/lib/test/subscription.test.d.ts.map +1 -0
- package/lib/test/subscription.test.js +99 -0
- package/lib/test/subscription.test.js.map +1 -0
- package/{dist → lib}/test/tools.test.d.ts +0 -0
- package/{dist → lib}/test/tools.test.d.ts.map +0 -0
- package/{dist → lib}/test/tools.test.js +0 -0
- package/{dist → lib}/test/tools.test.js.map +0 -0
- package/{dist → lib}/test/typed-json.test.d.ts +0 -0
- package/{dist → lib}/test/typed-json.test.d.ts.map +0 -0
- package/{dist → lib}/test/typed-json.test.js +0 -0
- package/{dist → lib}/test/typed-json.test.js.map +0 -0
- package/{dist → lib}/test/unions.test.d.ts +0 -0
- package/{dist → lib}/test/unions.test.d.ts.map +0 -0
- package/{dist → lib}/test/unions.test.js +0 -0
- package/{dist → lib}/test/unions.test.js.map +0 -0
- package/{dist → lib}/test/where.test.d.ts +0 -0
- package/{dist → lib}/test/where.test.d.ts.map +0 -0
- package/{dist → lib}/test/where.test.js +0 -0
- package/{dist → lib}/test/where.test.js.map +0 -0
- package/{dist → lib}/tools.d.ts +0 -0
- package/{dist → lib}/tools.d.ts.map +0 -0
- package/{dist → lib}/tools.js +3 -3
- package/{dist → lib}/tools.js.map +1 -1
- package/lib/util/error-handling.d.ts +11 -0
- package/lib/util/error-handling.d.ts.map +1 -0
- package/lib/util/error-handling.js +42 -0
- package/lib/util/error-handling.js.map +1 -0
- package/lib/util/execute.d.ts +5 -0
- package/lib/util/execute.d.ts.map +1 -0
- package/lib/util/execute.js +28 -0
- package/lib/util/execute.js.map +1 -0
- package/lib/util/lazy-transaction.d.ts +10 -0
- package/lib/util/lazy-transaction.d.ts.map +1 -0
- package/lib/util/lazy-transaction.js +43 -0
- package/lib/util/lazy-transaction.js.map +1 -0
- package/lib/util/limit.d.ts +11 -0
- package/lib/util/limit.d.ts.map +1 -0
- package/lib/util/limit.js +39 -0
- package/lib/util/limit.js.map +1 -0
- package/lib/util/resolve-tree.d.ts +14 -0
- package/lib/util/resolve-tree.d.ts.map +1 -0
- package/lib/util/resolve-tree.js +52 -0
- package/lib/util/resolve-tree.js.map +1 -0
- package/{dist → lib/util}/util.d.ts +2 -3
- package/lib/util/util.d.ts.map +1 -0
- package/{dist → lib/util}/util.js +9 -13
- package/lib/util/util.js.map +1 -0
- package/package.json +18 -10
- package/src/context.ts +17 -0
- package/src/db.ts +46 -57
- package/src/ir/args.ts +85 -0
- package/src/ir/connection.ts +48 -0
- package/src/ir/fields.ts +40 -0
- package/src/limit.size.ts +130 -0
- package/src/main.ts +74 -42
- package/src/{gql/schema.ts → model.schema.ts} +72 -9
- package/src/model.ts +13 -1
- package/src/{orderBy.ts → opencrud/orderBy.ts} +3 -10
- package/src/opencrud/schema.ts +639 -0
- package/src/opencrud/tree.ts +144 -0
- package/src/opencrud/where.ts +141 -0
- package/src/{gql/scalars → scalars}/BigInt.ts +1 -1
- package/src/{gql/scalars → scalars}/Bytes.ts +4 -4
- package/src/{gql/scalars → scalars}/DateTime.ts +1 -1
- package/src/{gql/scalars → scalars}/JSON.ts +0 -0
- package/src/{gql/scalars → scalars}/index.ts +0 -0
- package/src/server.ts +175 -55
- package/src/sql/cursor.ts +291 -0
- package/src/sql/mapping.ts +66 -0
- package/src/sql/printer.ts +328 -0
- package/src/sql/query.ts +194 -0
- package/src/sql/util.ts +89 -0
- package/src/subscription.ts +46 -0
- package/src/test/fts.test.ts +1 -1
- package/src/test/limits.test.ts +163 -0
- package/src/test/setup.ts +16 -11
- package/src/test/subscription.test.ts +98 -0
- package/src/tools.ts +1 -1
- package/src/util/error-handling.ts +40 -0
- package/src/util/execute.ts +53 -0
- package/src/util/lazy-transaction.ts +49 -0
- package/src/util/limit.ts +34 -0
- package/src/util/resolve-tree.ts +65 -0
- package/src/{util.ts → util/util.ts} +9 -14
- package/dist/db.d.ts +0 -28
- package/dist/db.d.ts.map +0 -1
- package/dist/db.js +0 -69
- package/dist/db.js.map +0 -1
- package/dist/gql/opencrud.d.ts +0 -6
- package/dist/gql/opencrud.d.ts.map +0 -1
- package/dist/gql/opencrud.js +0 -326
- package/dist/gql/opencrud.js.map +0 -1
- package/dist/gql/scalars/BigInt.d.ts.map +0 -1
- package/dist/gql/scalars/BigInt.js.map +0 -1
- package/dist/gql/scalars/Bytes.d.ts.map +0 -1
- package/dist/gql/scalars/Bytes.js.map +0 -1
- package/dist/gql/scalars/DateTime.d.ts.map +0 -1
- package/dist/gql/scalars/DateTime.js.map +0 -1
- package/dist/gql/scalars/JSON.d.ts.map +0 -1
- package/dist/gql/scalars/JSON.js.map +0 -1
- package/dist/gql/scalars/index.d.ts.map +0 -1
- package/dist/gql/scalars/index.js.map +0 -1
- package/dist/gql/schema.d.ts.map +0 -1
- package/dist/gql/schema.js.map +0 -1
- package/dist/main.js +0 -44
- package/dist/main.js.map +0 -1
- package/dist/model.d.ts.map +0 -1
- package/dist/orderBy.d.ts.map +0 -1
- package/dist/orderBy.js.map +0 -1
- package/dist/queryBuilder.d.ts +0 -56
- package/dist/queryBuilder.d.ts.map +0 -1
- package/dist/queryBuilder.js +0 -733
- package/dist/queryBuilder.js.map +0 -1
- package/dist/relayConnection.d.ts +0 -37
- package/dist/relayConnection.d.ts.map +0 -1
- package/dist/relayConnection.js +0 -43
- package/dist/relayConnection.js.map +0 -1
- package/dist/requestedFields.d.ts +0 -33
- package/dist/requestedFields.d.ts.map +0 -1
- package/dist/requestedFields.js +0 -179
- package/dist/requestedFields.js.map +0 -1
- package/dist/resolver.d.ts +0 -9
- package/dist/resolver.d.ts.map +0 -1
- package/dist/resolver.js +0 -158
- package/dist/resolver.js.map +0 -1
- package/dist/server.d.ts +0 -22
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -96
- package/dist/server.js.map +0 -1
- package/dist/test/fts.test.js.map +0 -1
- package/dist/test/setup.d.ts +0 -14
- package/dist/test/setup.d.ts.map +0 -1
- package/dist/test/setup.js.map +0 -1
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js.map +0 -1
- package/dist/where.d.ts +0 -9
- package/dist/where.d.ts.map +0 -1
- package/dist/where.js +0 -101
- package/dist/where.js.map +0 -1
- package/src/gql/opencrud.ts +0 -350
- package/src/queryBuilder.ts +0 -891
- package/src/relayConnection.ts +0 -80
- package/src/requestedFields.ts +0 -246
- package/src/resolver.ts +0 -201
- package/src/where.ts +0 -119
package/src/sql/query.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {assertNotNull} from "@subsquid/util-internal"
|
|
2
|
+
import assert from "assert"
|
|
3
|
+
import type {Dialect} from "../dialect"
|
|
4
|
+
import type {EntityListArguments, Where} from "../ir/args"
|
|
5
|
+
import {
|
|
6
|
+
decodeRelayConnectionCursor,
|
|
7
|
+
encodeRelayConnectionCursor,
|
|
8
|
+
RelayConnectionEdge,
|
|
9
|
+
RelayConnectionPageInfo,
|
|
10
|
+
RelayConnectionRequest,
|
|
11
|
+
RelayConnectionResponse
|
|
12
|
+
} from "../ir/connection"
|
|
13
|
+
import type {FieldRequest} from "../ir/fields"
|
|
14
|
+
import type {Model} from "../model"
|
|
15
|
+
import {toSafeInteger} from "../util/util"
|
|
16
|
+
import {mapRows} from "./mapping"
|
|
17
|
+
import {EntityListQueryPrinter} from "./printer"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export interface Query<T> {
|
|
21
|
+
readonly sql: string
|
|
22
|
+
readonly params: unknown[]
|
|
23
|
+
map(rows: any[][]): T
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
export class EntityListQuery implements Query<any[]> {
|
|
28
|
+
public readonly sql: string
|
|
29
|
+
public readonly params: unknown[] = []
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
model: Model,
|
|
33
|
+
dialect: Dialect,
|
|
34
|
+
entityName: string,
|
|
35
|
+
private fields: FieldRequest[],
|
|
36
|
+
args: EntityListArguments
|
|
37
|
+
) {
|
|
38
|
+
this.sql = new EntityListQueryPrinter(model, dialect, entityName, this.params, args, fields).print()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
map(rows: any[][]): any[] {
|
|
42
|
+
return mapRows(rows, this.fields)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
export class EntityByIdQuery {
|
|
48
|
+
public readonly sql: string
|
|
49
|
+
public readonly params: unknown[] = []
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
model: Model,
|
|
53
|
+
dialect: Dialect,
|
|
54
|
+
entityName: string,
|
|
55
|
+
private fields: FieldRequest[],
|
|
56
|
+
id: string
|
|
57
|
+
) {
|
|
58
|
+
this.sql = new EntityListQueryPrinter(
|
|
59
|
+
model,
|
|
60
|
+
dialect,
|
|
61
|
+
entityName,
|
|
62
|
+
this.params,
|
|
63
|
+
{where: {op: 'eq', field: 'id', value: id}},
|
|
64
|
+
fields
|
|
65
|
+
).print()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
map(rows: any[][]): any {
|
|
69
|
+
assert(rows.length < 2)
|
|
70
|
+
return mapRows(rows, this.fields)[0]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
export class EntityCountQuery implements Query<number> {
|
|
76
|
+
public readonly sql: string
|
|
77
|
+
public readonly params: unknown[] = []
|
|
78
|
+
|
|
79
|
+
constructor(
|
|
80
|
+
model: Model,
|
|
81
|
+
dialect: Dialect,
|
|
82
|
+
entityName: string,
|
|
83
|
+
where?: Where
|
|
84
|
+
) {
|
|
85
|
+
this.sql = 'SELECT count(*) ' + new EntityListQueryPrinter(model, dialect, entityName, this.params, {where}).printFrom()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
map(rows: any[][]): number {
|
|
89
|
+
return toCount(rows)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
export class EntityConnectionQuery implements Query<RelayConnectionResponse> {
|
|
95
|
+
public readonly sql: string
|
|
96
|
+
public readonly params: unknown[] = []
|
|
97
|
+
private offset = 0
|
|
98
|
+
private limit = 100
|
|
99
|
+
private edgeNode?: FieldRequest[]
|
|
100
|
+
private edgeCursor?: boolean
|
|
101
|
+
private pageInfo?: boolean
|
|
102
|
+
private totalCount?: boolean
|
|
103
|
+
|
|
104
|
+
constructor(
|
|
105
|
+
model: Model,
|
|
106
|
+
dialect: Dialect,
|
|
107
|
+
entityName: string,
|
|
108
|
+
req: RelayConnectionRequest
|
|
109
|
+
) {
|
|
110
|
+
this.setOffsetAndLimit(req)
|
|
111
|
+
this.edgeCursor = req.edgeCursor
|
|
112
|
+
this.pageInfo = req.pageInfo
|
|
113
|
+
this.totalCount = req.totalCount
|
|
114
|
+
|
|
115
|
+
let printer = new EntityListQueryPrinter(model, dialect, entityName, this.params, {
|
|
116
|
+
orderBy: req.orderBy,
|
|
117
|
+
where: req.where,
|
|
118
|
+
offset: this.offset,
|
|
119
|
+
limit: this.limit + 1
|
|
120
|
+
}, req.edgeNode)
|
|
121
|
+
|
|
122
|
+
if (req.edgeNode?.length) {
|
|
123
|
+
this.edgeNode = req.edgeNode
|
|
124
|
+
this.sql = printer.print()
|
|
125
|
+
} else {
|
|
126
|
+
this.sql = `SELECT count(*) FROM (SELECT true ${printer.printFrom()}) AS rows`
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private setOffsetAndLimit(req: RelayConnectionRequest): void {
|
|
131
|
+
if (req.after != null) {
|
|
132
|
+
this.offset = assertNotNull(decodeRelayConnectionCursor(req.after))
|
|
133
|
+
}
|
|
134
|
+
if (req.first != null) {
|
|
135
|
+
assert(req.first >= 0)
|
|
136
|
+
this.limit = req.first
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
map(rows: any[][]): RelayConnectionResponse {
|
|
141
|
+
let res: RelayConnectionResponse = {}
|
|
142
|
+
if (this.edgeNode) {
|
|
143
|
+
let nodes = mapRows(rows, this.edgeNode)
|
|
144
|
+
let edges: RelayConnectionEdge[] = new Array(Math.min(this.limit, nodes.length))
|
|
145
|
+
for (let i = 0; i < edges.length; i++) {
|
|
146
|
+
edges[i] = {
|
|
147
|
+
node: nodes[i],
|
|
148
|
+
cursor: this.edgeCursor ? encodeRelayConnectionCursor(this.offset + i + 1) : undefined
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
res.edges = edges
|
|
152
|
+
res.pageInfo = this.getPageInfo(nodes.length)
|
|
153
|
+
res.totalCount = this.getTotalCount(nodes.length)
|
|
154
|
+
} else {
|
|
155
|
+
let count = toCount(rows)
|
|
156
|
+
if (this.edgeCursor) {
|
|
157
|
+
res.edges = new Array(Math.min(this.limit, count))
|
|
158
|
+
for (let i = 0; i < res.edges.length; i++) {
|
|
159
|
+
res.edges[i] = {
|
|
160
|
+
cursor: encodeRelayConnectionCursor(this.offset + i + 1)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
res.pageInfo = this.getPageInfo(count)
|
|
165
|
+
res.totalCount = this.getTotalCount(count)
|
|
166
|
+
}
|
|
167
|
+
return res
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private getPageInfo(count: number): Partial<RelayConnectionPageInfo> | undefined {
|
|
171
|
+
if (!this.pageInfo) return
|
|
172
|
+
return {
|
|
173
|
+
hasNextPage: count > this.limit,
|
|
174
|
+
hasPreviousPage: count > 0 && this.offset > 0,
|
|
175
|
+
startCursor: count > 0 ? encodeRelayConnectionCursor(this.offset + 1) : '',
|
|
176
|
+
endCursor: count > 0 ? encodeRelayConnectionCursor(this.offset + Math.min(this.limit, count)) : ''
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private getTotalCount(count: number): number | undefined {
|
|
181
|
+
if (!this.totalCount) return
|
|
182
|
+
if (count > 0 && count <= this.limit) {
|
|
183
|
+
return this.offset + count
|
|
184
|
+
} else {
|
|
185
|
+
return undefined
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
function toCount(rows: any[][]): number {
|
|
192
|
+
assert(rows.length == 1)
|
|
193
|
+
return toSafeInteger(rows[0][0])
|
|
194
|
+
}
|
package/src/sql/util.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type {Dialect} from "../dialect"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function escapeIdentifier(dialect: Dialect, name: string): string {
|
|
5
|
+
return `"${name.replace(/"/g, '""')}"`
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export class ColumnSet {
|
|
10
|
+
private columns: Map<string, number> = new Map()
|
|
11
|
+
|
|
12
|
+
add(column: string): number {
|
|
13
|
+
let idx = this.columns.get(column)
|
|
14
|
+
if (idx == null) {
|
|
15
|
+
idx = this.columns.size
|
|
16
|
+
this.columns.set(column, idx)
|
|
17
|
+
}
|
|
18
|
+
return idx
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
names(): string[] {
|
|
22
|
+
return [...this.columns.keys()]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
size(): number {
|
|
26
|
+
return this.columns.size
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* LEFT OUTER JOIN "{table}" "{alias}" ON "{alias}"."{column}" = {rhs}
|
|
33
|
+
*/
|
|
34
|
+
export interface Join {
|
|
35
|
+
table: string
|
|
36
|
+
alias: string
|
|
37
|
+
column: string
|
|
38
|
+
rhs: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
export class JoinSet {
|
|
43
|
+
private joins: Map<string, Join> = new Map()
|
|
44
|
+
|
|
45
|
+
constructor(private aliases: AliasSet) {
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
add(table: string, column: string, rhs: string): string {
|
|
49
|
+
let key = `${table} ${column} ${rhs}`
|
|
50
|
+
let e = this.joins.get(key)
|
|
51
|
+
if (!e) {
|
|
52
|
+
e = {
|
|
53
|
+
table,
|
|
54
|
+
alias: this.aliases.add(table),
|
|
55
|
+
column,
|
|
56
|
+
rhs
|
|
57
|
+
}
|
|
58
|
+
this.joins.set(key, e)
|
|
59
|
+
}
|
|
60
|
+
return e.alias
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
forEach(cb: (join: Join) => void): void {
|
|
64
|
+
this.joins.forEach(join => cb(join))
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
export class AliasSet {
|
|
70
|
+
private aliases: Record<string, number> = {}
|
|
71
|
+
|
|
72
|
+
add(name: string): string {
|
|
73
|
+
if (this.aliases[name]) {
|
|
74
|
+
return name + "_" + (this.aliases[name]++)
|
|
75
|
+
} else {
|
|
76
|
+
this.aliases[name] = 1
|
|
77
|
+
return name
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
export function printClause(op: string, exps: string[]): string {
|
|
84
|
+
switch(exps.length) {
|
|
85
|
+
case 0: return ''
|
|
86
|
+
case 1: return exps[0]
|
|
87
|
+
default: return exps.join(' ' + op + ' ')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import deepEqual from "deep-equal"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export class Subscription<T> implements AsyncIterator<T>, AsyncIterable<T> {
|
|
5
|
+
private timer?: NodeJS.Timer
|
|
6
|
+
private prev?: T
|
|
7
|
+
private hasNoVal = true
|
|
8
|
+
|
|
9
|
+
constructor(private interval: number, private poll: () => Promise<T>) {}
|
|
10
|
+
|
|
11
|
+
[Symbol.asyncIterator]() {
|
|
12
|
+
return this
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async next() {
|
|
16
|
+
if (this.hasNoVal) {
|
|
17
|
+
this.prev = await this.poll()
|
|
18
|
+
this.hasNoVal = false
|
|
19
|
+
}
|
|
20
|
+
let value
|
|
21
|
+
do {
|
|
22
|
+
await new Promise(resolve => {
|
|
23
|
+
this.timer = setTimeout(resolve, this.interval)
|
|
24
|
+
})
|
|
25
|
+
value = await this.poll()
|
|
26
|
+
} while (deepEqual(this.prev, value))
|
|
27
|
+
this.prev = value
|
|
28
|
+
return {done: false, value}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async return() {
|
|
32
|
+
if (this.timer != null) {
|
|
33
|
+
clearTimeout(this.timer)
|
|
34
|
+
this.timer = undefined
|
|
35
|
+
}
|
|
36
|
+
return EOS
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const EOS = {
|
|
42
|
+
done: true,
|
|
43
|
+
get value(): any {
|
|
44
|
+
throw new Error('Unexpected value access')
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/test/fts.test.ts
CHANGED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import expect from 'expect'
|
|
2
|
+
import {useDatabase, useServer} from './setup'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
describe('response size limits', function() {
|
|
6
|
+
useDatabase([
|
|
7
|
+
`create table "order1" (id text primary key)`,
|
|
8
|
+
`create table item1 (id text primary key, order_id text, name text)`,
|
|
9
|
+
`create table "order2" (id text primary key)`,
|
|
10
|
+
`create table item2 (id text primary key, order_id text, name text)`,
|
|
11
|
+
`create table "order3" (id text primary key)`,
|
|
12
|
+
`create table item3 (id text primary key, order_id text, name text)`,
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
const client = useServer(`
|
|
16
|
+
type Order1 @entity {
|
|
17
|
+
id: ID!
|
|
18
|
+
items: [Item1!]! @derivedFrom(field: "order")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type Item1 @entity {
|
|
22
|
+
id: ID!
|
|
23
|
+
order: Order1!
|
|
24
|
+
name: String
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type Order2 @entity @cardinality(value: 10) {
|
|
28
|
+
id: ID!
|
|
29
|
+
items: [Item2!]! @derivedFrom(field: "order")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type Item2 @entity {
|
|
33
|
+
id: ID!
|
|
34
|
+
order: Order2!
|
|
35
|
+
name: String
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type Order3 @entity {
|
|
39
|
+
id: ID!
|
|
40
|
+
items: [Item3!]! @derivedFrom(field: "order") @cardinality(value: 10)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type Item3 @entity {
|
|
44
|
+
id: ID!
|
|
45
|
+
order: Order3!
|
|
46
|
+
name: String @byteWeight(value: 10.0)
|
|
47
|
+
}
|
|
48
|
+
`, {
|
|
49
|
+
maxResponseNodes: 50,
|
|
50
|
+
maxRootFields: 3
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('unlimited requests fail', async function() {
|
|
54
|
+
let result = await client.query(`
|
|
55
|
+
query {
|
|
56
|
+
order1s {
|
|
57
|
+
id
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
`)
|
|
61
|
+
expect(result).toMatchObject({
|
|
62
|
+
data: null,
|
|
63
|
+
errors: [
|
|
64
|
+
expect.objectContaining({message: 'response might exceed the size limit', path: ['order1s']})
|
|
65
|
+
]
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('limited requests work', function() {
|
|
70
|
+
return client.test(`
|
|
71
|
+
query {
|
|
72
|
+
order1s(limit: 10) {
|
|
73
|
+
items(limit: 2) {
|
|
74
|
+
id
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`, {
|
|
79
|
+
order1s: []
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('entity level cardinalities are respected', function() {
|
|
84
|
+
return client.test(`
|
|
85
|
+
query {
|
|
86
|
+
order2s {
|
|
87
|
+
id
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`, {
|
|
91
|
+
order2s: []
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('item cardinalities are respected', function() {
|
|
96
|
+
return client.test(`
|
|
97
|
+
query {
|
|
98
|
+
order3s(limit: 1) {
|
|
99
|
+
items { id }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
`, {
|
|
103
|
+
order3s: []
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('@byteWeight annotations are respected', async function() {
|
|
108
|
+
let result = await client.query(`
|
|
109
|
+
query {
|
|
110
|
+
order3s(limit: 1) {
|
|
111
|
+
items(limit: 8) { name }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
`)
|
|
115
|
+
expect(result).toEqual({
|
|
116
|
+
data: null,
|
|
117
|
+
errors: [
|
|
118
|
+
expect.objectContaining({
|
|
119
|
+
message: 'response might exceed the size limit',
|
|
120
|
+
path: ['order3s']
|
|
121
|
+
})
|
|
122
|
+
]
|
|
123
|
+
})
|
|
124
|
+
await client.test(`
|
|
125
|
+
query {
|
|
126
|
+
order3s(limit: 1) {
|
|
127
|
+
items(limit: 4) { name }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
`, {
|
|
131
|
+
order3s: []
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('id_in conditions are understood', function() {
|
|
136
|
+
return client.test(`
|
|
137
|
+
query {
|
|
138
|
+
order1s(where: {id_in: ["1", "2", "3"]}) {
|
|
139
|
+
id
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
`, {
|
|
143
|
+
order1s: []
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('root query fields limit', async function() {
|
|
148
|
+
return client.errorTest(`
|
|
149
|
+
query {
|
|
150
|
+
a: order1ById(id: "1") { id }
|
|
151
|
+
b: order1ById(id: "1") { id }
|
|
152
|
+
c: order1ById(id: "1") { id }
|
|
153
|
+
d: order1ById(id: "1") { id }
|
|
154
|
+
}
|
|
155
|
+
`, {
|
|
156
|
+
errors: [
|
|
157
|
+
expect.objectContaining({
|
|
158
|
+
message: 'only 3 root query fields allowed, but got 4'
|
|
159
|
+
})
|
|
160
|
+
]
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
})
|
package/src/test/setup.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {assertNotNull} from "@subsquid/util-internal"
|
|
2
|
+
import {ListeningServer} from "@subsquid/util-internal-http-server"
|
|
2
3
|
import {Client} from "gql-test-client"
|
|
3
4
|
import {parse} from "graphql"
|
|
4
5
|
import {Client as PgClient, ClientBase, Pool} from "pg"
|
|
5
|
-
import {buildModel, buildSchema} from "../
|
|
6
|
-
import {
|
|
6
|
+
import {buildModel, buildSchema} from "../model.schema"
|
|
7
|
+
import {serve, ServerOptions} from '../server'
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
export function isCockroach(): boolean {
|
|
@@ -22,7 +23,7 @@ export const db_config = {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
async function
|
|
26
|
+
export async function withDatabase(block: (client: ClientBase) => Promise<void>): Promise<void> {
|
|
26
27
|
let client = new PgClient(db_config)
|
|
27
28
|
await client.connect()
|
|
28
29
|
try {
|
|
@@ -33,8 +34,8 @@ async function withClient(block: (client: ClientBase) => Promise<void>): Promise
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
export function
|
|
37
|
-
return
|
|
37
|
+
export function databaseExecute(sql: string[]): Promise<void> {
|
|
38
|
+
return withDatabase(async client => {
|
|
38
39
|
for (let i = 0; i < sql.length; i++) {
|
|
39
40
|
await client.query(sql[i])
|
|
40
41
|
}
|
|
@@ -43,7 +44,7 @@ export function databaseInit(sql: string[]): Promise<void> {
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
export function databaseDelete(): Promise<void> {
|
|
46
|
-
return
|
|
47
|
+
return withDatabase(async client => {
|
|
47
48
|
await client.query(`DROP SCHEMA IF EXISTS root CASCADE`)
|
|
48
49
|
await client.query(`CREATE SCHEMA root`)
|
|
49
50
|
})
|
|
@@ -53,25 +54,29 @@ export function databaseDelete(): Promise<void> {
|
|
|
53
54
|
export function useDatabase(sql: string[]): void {
|
|
54
55
|
before(async () => {
|
|
55
56
|
await databaseDelete()
|
|
56
|
-
await
|
|
57
|
+
await databaseExecute(sql)
|
|
57
58
|
})
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
export function useServer(schema: string): Client {
|
|
62
|
+
export function useServer(schema: string, options?: Partial<ServerOptions>): Client {
|
|
62
63
|
let client = new Client('not defined')
|
|
63
64
|
let db = new Pool(db_config)
|
|
64
65
|
let info: ListeningServer | undefined
|
|
65
66
|
before(async () => {
|
|
66
67
|
info = await serve({
|
|
67
|
-
db,
|
|
68
|
+
connection: db,
|
|
68
69
|
model: buildModel(buildSchema(parse(schema))),
|
|
69
70
|
port: 0,
|
|
70
|
-
dialect: isCockroach() ? 'cockroach' : 'postgres'
|
|
71
|
+
dialect: isCockroach() ? 'cockroach' : 'postgres',
|
|
72
|
+
subscriptions: true,
|
|
73
|
+
subscriptionPollInterval: 500,
|
|
74
|
+
maxRootFields: 10,
|
|
75
|
+
...options
|
|
71
76
|
})
|
|
72
77
|
client.endpoint = `http://localhost:${info.port}/graphql`
|
|
73
78
|
})
|
|
74
|
-
after(() => info?.
|
|
79
|
+
after(() => info?.close())
|
|
75
80
|
after(() => db.end())
|
|
76
81
|
return client
|
|
77
82
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {wait} from "@subsquid/util-internal"
|
|
2
|
+
import expect from "expect"
|
|
3
|
+
import {databaseExecute, useDatabase, useServer} from "./setup"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
describe("subscriptions", function() {
|
|
7
|
+
useDatabase([
|
|
8
|
+
`create table "order" (id text primary key, kind text not null, name text)`,
|
|
9
|
+
`create table item (id text primary key, order_id text, name text)`,
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
const client = useServer(`
|
|
13
|
+
type Order @entity {
|
|
14
|
+
id: ID!
|
|
15
|
+
kind: String!
|
|
16
|
+
name: String
|
|
17
|
+
items: [Item!]! @derivedFrom(field: "order")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type Item @entity {
|
|
21
|
+
id: ID!
|
|
22
|
+
order: Order
|
|
23
|
+
name: String
|
|
24
|
+
}
|
|
25
|
+
`)
|
|
26
|
+
|
|
27
|
+
it("entity list", function() {
|
|
28
|
+
return client.subscriptionTest(`
|
|
29
|
+
subscription {
|
|
30
|
+
orders(where: {kind_eq: "list"}, orderBy: id_ASC) {
|
|
31
|
+
id
|
|
32
|
+
name
|
|
33
|
+
items(orderBy: id_ASC) {
|
|
34
|
+
name
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`, async take => {
|
|
39
|
+
await wait(1000)
|
|
40
|
+
await databaseExecute([
|
|
41
|
+
`insert into "order" (id, kind) values ('1', 'list')`,
|
|
42
|
+
`insert into "order" (id, kind) values ('2', 'foo')`,
|
|
43
|
+
])
|
|
44
|
+
expect(await take()).toEqual({
|
|
45
|
+
data: {
|
|
46
|
+
orders: [{id: '1', name: null, items: []}]
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
await databaseExecute([`
|
|
50
|
+
update "order" set name = 'hello' where id in ('1', '2');
|
|
51
|
+
insert into item (id, "order_id", name) values ('1-1', '1', '123')
|
|
52
|
+
`])
|
|
53
|
+
expect(await take()).toEqual({
|
|
54
|
+
data: {
|
|
55
|
+
orders: [
|
|
56
|
+
{id: '1', name: 'hello', items: [{name: '123'}]}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it("entity by id", async function() {
|
|
64
|
+
await databaseExecute([
|
|
65
|
+
`insert into "order" (id, kind) values ('3', 'by id')`,
|
|
66
|
+
`insert into item (id, "order_id", name) values ('3-1', '3', 'hello')`
|
|
67
|
+
])
|
|
68
|
+
await client.subscriptionTest(`
|
|
69
|
+
subscription {
|
|
70
|
+
third: orderById(id: "3") {
|
|
71
|
+
name
|
|
72
|
+
items(orderBy: id_ASC) {
|
|
73
|
+
name
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
`, async take => {
|
|
78
|
+
await wait(1000)
|
|
79
|
+
await databaseExecute([`
|
|
80
|
+
start transaction;
|
|
81
|
+
update "order" set name = 'foo' where id = '3';
|
|
82
|
+
insert into item (id, "order_id", name) values ('3-2', '3', 'world');
|
|
83
|
+
commit;
|
|
84
|
+
`])
|
|
85
|
+
expect(await take()).toEqual({
|
|
86
|
+
data: {
|
|
87
|
+
third: {
|
|
88
|
+
name: 'foo',
|
|
89
|
+
items: [
|
|
90
|
+
{name: 'hello'},
|
|
91
|
+
{name: 'world'}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
})
|
package/src/tools.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as fs from "fs"
|
|
|
3
3
|
import * as path from "path"
|
|
4
4
|
import {parse, Source} from "graphql"
|
|
5
5
|
import process from "process"
|
|
6
|
-
import {buildModel, buildSchema} from "./
|
|
6
|
+
import {buildModel, buildSchema} from "./model.schema"
|
|
7
7
|
import type {Model} from "./model"
|
|
8
8
|
|
|
9
9
|
|