@subsquid/openreader 1.0.1 → 2.0.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 (279) hide show
  1. package/bin/main.js +2 -1
  2. package/lib/context.d.ts +11 -0
  3. package/lib/context.d.ts.map +1 -0
  4. package/lib/context.js +3 -0
  5. package/lib/context.js.map +1 -0
  6. package/lib/db.d.ts +23 -0
  7. package/lib/db.d.ts.map +1 -0
  8. package/lib/db.js +57 -0
  9. package/lib/db.js.map +1 -0
  10. package/{dist → lib}/dialect.d.ts +0 -0
  11. package/{dist → lib}/dialect.d.ts.map +0 -0
  12. package/{dist → lib}/dialect.js +0 -0
  13. package/{dist → lib}/dialect.js.map +0 -0
  14. package/lib/ir/args.d.ts +47 -0
  15. package/lib/ir/args.d.ts.map +1 -0
  16. package/lib/ir/args.js +3 -0
  17. package/lib/ir/args.js.map +1 -0
  18. package/lib/ir/connection.d.ts +30 -0
  19. package/lib/ir/connection.d.ts.map +1 -0
  20. package/lib/ir/connection.js +17 -0
  21. package/lib/ir/connection.js.map +1 -0
  22. package/lib/ir/fields.d.ts +22 -0
  23. package/lib/ir/fields.d.ts.map +1 -0
  24. package/lib/ir/fields.js +3 -0
  25. package/lib/ir/fields.js.map +1 -0
  26. package/lib/limit.size.d.ts +3 -0
  27. package/lib/limit.size.d.ts.map +1 -0
  28. package/lib/limit.size.js +44 -0
  29. package/lib/limit.size.js.map +1 -0
  30. package/{dist → lib}/main.d.ts +0 -0
  31. package/{dist → lib}/main.d.ts.map +0 -0
  32. package/lib/main.js +53 -0
  33. package/lib/main.js.map +1 -0
  34. package/{dist → lib}/model.d.ts +10 -1
  35. package/lib/model.d.ts.map +1 -0
  36. package/{dist → lib}/model.js +0 -0
  37. package/{dist → lib}/model.js.map +0 -0
  38. package/{dist/gql/schema.d.ts → lib/model.schema.d.ts} +2 -2
  39. package/lib/model.schema.d.ts.map +1 -0
  40. package/{dist/gql/schema.js → lib/model.schema.js} +44 -7
  41. package/lib/model.schema.js.map +1 -0
  42. package/{dist → lib}/model.tools.d.ts +0 -0
  43. package/{dist → lib}/model.tools.d.ts.map +0 -0
  44. package/{dist → lib}/model.tools.js +0 -0
  45. package/{dist → lib}/model.tools.js.map +0 -0
  46. package/{dist → lib/opencrud}/orderBy.d.ts +2 -5
  47. package/lib/opencrud/orderBy.d.ts.map +1 -0
  48. package/{dist → lib/opencrud}/orderBy.js +1 -1
  49. package/lib/opencrud/orderBy.js.map +1 -0
  50. package/lib/opencrud/schema.d.ts +31 -0
  51. package/lib/opencrud/schema.d.ts.map +1 -0
  52. package/lib/opencrud/schema.js +522 -0
  53. package/lib/opencrud/schema.js.map +1 -0
  54. package/lib/opencrud/tree.d.ts +8 -0
  55. package/lib/opencrud/tree.d.ts.map +1 -0
  56. package/lib/opencrud/tree.js +131 -0
  57. package/lib/opencrud/tree.js.map +1 -0
  58. package/lib/opencrud/where.d.ts +7 -0
  59. package/lib/opencrud/where.d.ts.map +1 -0
  60. package/lib/opencrud/where.js +141 -0
  61. package/lib/opencrud/where.js.map +1 -0
  62. package/{dist/gql → lib}/scalars/BigInt.d.ts +0 -0
  63. package/lib/scalars/BigInt.d.ts.map +1 -0
  64. package/{dist/gql → lib}/scalars/BigInt.js +1 -1
  65. package/lib/scalars/BigInt.js.map +1 -0
  66. package/{dist/gql → lib}/scalars/Bytes.d.ts +0 -0
  67. package/lib/scalars/Bytes.d.ts.map +1 -0
  68. package/{dist/gql → lib}/scalars/Bytes.js +2 -2
  69. package/lib/scalars/Bytes.js.map +1 -0
  70. package/{dist/gql → lib}/scalars/DateTime.d.ts +0 -0
  71. package/lib/scalars/DateTime.d.ts.map +1 -0
  72. package/{dist/gql → lib}/scalars/DateTime.js +1 -1
  73. package/lib/scalars/DateTime.js.map +1 -0
  74. package/{dist/gql → lib}/scalars/JSON.d.ts +0 -0
  75. package/lib/scalars/JSON.d.ts.map +1 -0
  76. package/{dist/gql → lib}/scalars/JSON.js +0 -0
  77. package/lib/scalars/JSON.js.map +1 -0
  78. package/{dist/gql → lib}/scalars/index.d.ts +0 -0
  79. package/lib/scalars/index.d.ts.map +1 -0
  80. package/{dist/gql → lib}/scalars/index.js +0 -0
  81. package/lib/scalars/index.js.map +1 -0
  82. package/lib/server.d.ts +38 -0
  83. package/lib/server.d.ts.map +1 -0
  84. package/lib/server.js +146 -0
  85. package/lib/server.js.map +1 -0
  86. package/lib/sql/cursor.d.ts +52 -0
  87. package/lib/sql/cursor.d.ts.map +1 -0
  88. package/lib/sql/cursor.js +234 -0
  89. package/lib/sql/cursor.js.map +1 -0
  90. package/lib/sql/mapping.d.ts +4 -0
  91. package/lib/sql/mapping.d.ts.map +1 -0
  92. package/lib/sql/mapping.js +71 -0
  93. package/lib/sql/mapping.js.map +1 -0
  94. package/lib/sql/printer.d.ts +37 -0
  95. package/lib/sql/printer.d.ts.map +1 -0
  96. package/lib/sql/printer.js +311 -0
  97. package/lib/sql/printer.js.map +1 -0
  98. package/lib/sql/query.d.ts +46 -0
  99. package/lib/sql/query.d.ts.map +1 -0
  100. package/lib/sql/query.js +134 -0
  101. package/lib/sql/query.js.map +1 -0
  102. package/lib/sql/util.d.ts +30 -0
  103. package/lib/sql/util.d.ts.map +1 -0
  104. package/lib/sql/util.js +75 -0
  105. package/lib/sql/util.js.map +1 -0
  106. package/lib/subscription.d.ts +18 -0
  107. package/lib/subscription.d.ts.map +1 -0
  108. package/lib/subscription.js +47 -0
  109. package/lib/subscription.js.map +1 -0
  110. package/{dist → lib}/test/basic.test.d.ts +0 -0
  111. package/{dist → lib}/test/basic.test.d.ts.map +0 -0
  112. package/{dist → lib}/test/basic.test.js +0 -0
  113. package/{dist → lib}/test/basic.test.js.map +0 -0
  114. package/{dist → lib}/test/connection.test.d.ts +0 -0
  115. package/{dist → lib}/test/connection.test.d.ts.map +0 -0
  116. package/{dist → lib}/test/connection.test.js +0 -0
  117. package/{dist → lib}/test/connection.test.js.map +0 -0
  118. package/{dist → lib}/test/fts.test.d.ts +0 -0
  119. package/{dist → lib}/test/fts.test.d.ts.map +0 -0
  120. package/{dist → lib}/test/fts.test.js +1 -1
  121. package/lib/test/fts.test.js.map +1 -0
  122. package/{dist → lib}/test/isNull.test.d.ts +0 -0
  123. package/{dist → lib}/test/isNull.test.d.ts.map +0 -0
  124. package/{dist → lib}/test/isNull.test.js +0 -0
  125. package/{dist → lib}/test/isNull.test.js.map +0 -0
  126. package/{dist → lib}/test/lists.test.d.ts +0 -0
  127. package/{dist → lib}/test/lists.test.d.ts.map +0 -0
  128. package/{dist → lib}/test/lists.test.js +0 -0
  129. package/{dist → lib}/test/lists.test.js.map +0 -0
  130. package/{dist → lib}/test/lookup.test.d.ts +0 -0
  131. package/{dist → lib}/test/lookup.test.d.ts.map +0 -0
  132. package/{dist → lib}/test/lookup.test.js +0 -0
  133. package/{dist → lib}/test/lookup.test.js.map +0 -0
  134. package/{dist → lib}/test/regressions.test.d.ts +0 -0
  135. package/{dist → lib}/test/regressions.test.d.ts.map +0 -0
  136. package/{dist → lib}/test/regressions.test.js +0 -0
  137. package/{dist → lib}/test/regressions.test.js.map +0 -0
  138. package/{dist → lib}/test/scalars.test.d.ts +0 -0
  139. package/{dist → lib}/test/scalars.test.d.ts.map +0 -0
  140. package/{dist → lib}/test/scalars.test.js +0 -0
  141. package/{dist → lib}/test/scalars.test.js.map +0 -0
  142. package/{dist → lib}/test/setup.d.ts +3 -1
  143. package/lib/test/setup.d.ts.map +1 -0
  144. package/{dist → lib}/test/setup.js +14 -12
  145. package/lib/test/setup.js.map +1 -0
  146. package/lib/test/subscription.test.d.ts +2 -0
  147. package/lib/test/subscription.test.d.ts.map +1 -0
  148. package/lib/test/subscription.test.js +99 -0
  149. package/lib/test/subscription.test.js.map +1 -0
  150. package/{dist → lib}/test/tools.test.d.ts +0 -0
  151. package/{dist → lib}/test/tools.test.d.ts.map +0 -0
  152. package/{dist → lib}/test/tools.test.js +0 -0
  153. package/{dist → lib}/test/tools.test.js.map +0 -0
  154. package/{dist → lib}/test/typed-json.test.d.ts +0 -0
  155. package/{dist → lib}/test/typed-json.test.d.ts.map +0 -0
  156. package/{dist → lib}/test/typed-json.test.js +0 -0
  157. package/{dist → lib}/test/typed-json.test.js.map +0 -0
  158. package/{dist → lib}/test/unions.test.d.ts +0 -0
  159. package/{dist → lib}/test/unions.test.d.ts.map +0 -0
  160. package/{dist → lib}/test/unions.test.js +0 -0
  161. package/{dist → lib}/test/unions.test.js.map +0 -0
  162. package/{dist → lib}/test/where.test.d.ts +0 -0
  163. package/{dist → lib}/test/where.test.d.ts.map +0 -0
  164. package/{dist → lib}/test/where.test.js +0 -0
  165. package/{dist → lib}/test/where.test.js.map +0 -0
  166. package/{dist → lib}/tools.d.ts +0 -0
  167. package/{dist → lib}/tools.d.ts.map +0 -0
  168. package/{dist → lib}/tools.js +3 -3
  169. package/{dist → lib}/tools.js.map +1 -1
  170. package/lib/util/error-handling.d.ts +11 -0
  171. package/lib/util/error-handling.d.ts.map +1 -0
  172. package/lib/util/error-handling.js +42 -0
  173. package/lib/util/error-handling.js.map +1 -0
  174. package/lib/util/lazy-transaction.d.ts +10 -0
  175. package/lib/util/lazy-transaction.d.ts.map +1 -0
  176. package/lib/util/lazy-transaction.js +43 -0
  177. package/lib/util/lazy-transaction.js.map +1 -0
  178. package/lib/util/resolve-tree.d.ts +14 -0
  179. package/lib/util/resolve-tree.d.ts.map +1 -0
  180. package/lib/util/resolve-tree.js +52 -0
  181. package/lib/util/resolve-tree.js.map +1 -0
  182. package/{dist → lib/util}/util.d.ts +2 -3
  183. package/lib/util/util.d.ts.map +1 -0
  184. package/{dist → lib/util}/util.js +9 -13
  185. package/lib/util/util.js.map +1 -0
  186. package/package.json +17 -9
  187. package/src/context.ts +14 -0
  188. package/src/db.ts +46 -57
  189. package/src/ir/args.ts +85 -0
  190. package/src/ir/connection.ts +48 -0
  191. package/src/ir/fields.ts +40 -0
  192. package/src/limit.size.ts +46 -0
  193. package/src/main.ts +62 -37
  194. package/src/{gql/schema.ts → model.schema.ts} +51 -8
  195. package/src/model.ts +12 -1
  196. package/src/{orderBy.ts → opencrud/orderBy.ts} +3 -10
  197. package/src/opencrud/schema.ts +632 -0
  198. package/src/opencrud/tree.ts +144 -0
  199. package/src/opencrud/where.ts +141 -0
  200. package/src/{gql/scalars → scalars}/BigInt.ts +1 -1
  201. package/src/{gql/scalars → scalars}/Bytes.ts +4 -4
  202. package/src/{gql/scalars → scalars}/DateTime.ts +1 -1
  203. package/src/{gql/scalars → scalars}/JSON.ts +0 -0
  204. package/src/{gql/scalars → scalars}/index.ts +0 -0
  205. package/src/server.ts +128 -48
  206. package/src/sql/cursor.ts +291 -0
  207. package/src/sql/mapping.ts +66 -0
  208. package/src/sql/printer.ts +328 -0
  209. package/src/sql/query.ts +194 -0
  210. package/src/sql/util.ts +89 -0
  211. package/src/subscription.ts +46 -0
  212. package/src/test/fts.test.ts +1 -1
  213. package/src/test/setup.ts +12 -10
  214. package/src/test/subscription.test.ts +98 -0
  215. package/src/tools.ts +1 -1
  216. package/src/util/error-handling.ts +40 -0
  217. package/src/util/lazy-transaction.ts +49 -0
  218. package/src/util/resolve-tree.ts +65 -0
  219. package/src/{util.ts → util/util.ts} +9 -14
  220. package/dist/db.d.ts +0 -28
  221. package/dist/db.d.ts.map +0 -1
  222. package/dist/db.js +0 -69
  223. package/dist/db.js.map +0 -1
  224. package/dist/gql/opencrud.d.ts +0 -6
  225. package/dist/gql/opencrud.d.ts.map +0 -1
  226. package/dist/gql/opencrud.js +0 -326
  227. package/dist/gql/opencrud.js.map +0 -1
  228. package/dist/gql/scalars/BigInt.d.ts.map +0 -1
  229. package/dist/gql/scalars/BigInt.js.map +0 -1
  230. package/dist/gql/scalars/Bytes.d.ts.map +0 -1
  231. package/dist/gql/scalars/Bytes.js.map +0 -1
  232. package/dist/gql/scalars/DateTime.d.ts.map +0 -1
  233. package/dist/gql/scalars/DateTime.js.map +0 -1
  234. package/dist/gql/scalars/JSON.d.ts.map +0 -1
  235. package/dist/gql/scalars/JSON.js.map +0 -1
  236. package/dist/gql/scalars/index.d.ts.map +0 -1
  237. package/dist/gql/scalars/index.js.map +0 -1
  238. package/dist/gql/schema.d.ts.map +0 -1
  239. package/dist/gql/schema.js.map +0 -1
  240. package/dist/main.js +0 -43
  241. package/dist/main.js.map +0 -1
  242. package/dist/model.d.ts.map +0 -1
  243. package/dist/orderBy.d.ts.map +0 -1
  244. package/dist/orderBy.js.map +0 -1
  245. package/dist/queryBuilder.d.ts +0 -56
  246. package/dist/queryBuilder.d.ts.map +0 -1
  247. package/dist/queryBuilder.js +0 -733
  248. package/dist/queryBuilder.js.map +0 -1
  249. package/dist/relayConnection.d.ts +0 -37
  250. package/dist/relayConnection.d.ts.map +0 -1
  251. package/dist/relayConnection.js +0 -43
  252. package/dist/relayConnection.js.map +0 -1
  253. package/dist/requestedFields.d.ts +0 -33
  254. package/dist/requestedFields.d.ts.map +0 -1
  255. package/dist/requestedFields.js +0 -179
  256. package/dist/requestedFields.js.map +0 -1
  257. package/dist/resolver.d.ts +0 -9
  258. package/dist/resolver.d.ts.map +0 -1
  259. package/dist/resolver.js +0 -158
  260. package/dist/resolver.js.map +0 -1
  261. package/dist/server.d.ts +0 -22
  262. package/dist/server.d.ts.map +0 -1
  263. package/dist/server.js +0 -96
  264. package/dist/server.js.map +0 -1
  265. package/dist/test/fts.test.js.map +0 -1
  266. package/dist/test/setup.d.ts.map +0 -1
  267. package/dist/test/setup.js.map +0 -1
  268. package/dist/util.d.ts.map +0 -1
  269. package/dist/util.js.map +0 -1
  270. package/dist/where.d.ts +0 -9
  271. package/dist/where.d.ts.map +0 -1
  272. package/dist/where.js +0 -101
  273. package/dist/where.js.map +0 -1
  274. package/src/gql/opencrud.ts +0 -350
  275. package/src/queryBuilder.ts +0 -891
  276. package/src/relayConnection.ts +0 -80
  277. package/src/requestedFields.ts +0 -246
  278. package/src/resolver.ts +0 -201
  279. package/src/where.ts +0 -119
@@ -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
+ }
@@ -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
+ }
@@ -6,7 +6,7 @@ function tsvector(columns: string[]) {
6
6
  }
7
7
 
8
8
 
9
- isCockroach() || describe('full text search', function () {
9
+ isCockroach() || describe.skip('full text search', function () {
10
10
  useDatabase([
11
11
  `create table foo (
12
12
  id text primary key,
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 "../gql/schema"
6
- import {ListeningServer, serve} from "../server"
6
+ import {buildModel, buildSchema} from "../model.schema"
7
+ import {serve} 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 withClient(block: (client: ClientBase) => Promise<void>): Promise<void> {
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 databaseInit(sql: string[]): Promise<void> {
37
- return withClient(async client => {
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 withClient(async client => {
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,7 +54,7 @@ export function databaseDelete(): Promise<void> {
53
54
  export function useDatabase(sql: string[]): void {
54
55
  before(async () => {
55
56
  await databaseDelete()
56
- await databaseInit(sql)
57
+ await databaseExecute(sql)
57
58
  })
58
59
  }
59
60
 
@@ -64,14 +65,15 @@ export function useServer(schema: string): Client {
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
71
73
  })
72
74
  client.endpoint = `http://localhost:${info.port}/graphql`
73
75
  })
74
- after(() => info?.stop())
76
+ after(() => info?.close())
75
77
  after(() => db.end())
76
78
  return client
77
79
  }
@@ -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 "./gql/schema"
6
+ import {buildModel, buildSchema} from "./model.schema"
7
7
  import type {Model} from "./model"
8
8
 
9
9
 
@@ -0,0 +1,40 @@
1
+ import {Logger, LogLevel} from "@subsquid/logger"
2
+ import {GraphQLError, printError} from "graphql"
3
+
4
+
5
+ export function logGraphQLError(log: Logger, err: GraphQLError, level: LogLevel = LogLevel.ERROR): void {
6
+ if (log.level > level) return
7
+ let msg = printError(err) + '\n'
8
+ log.write(level, {
9
+ graphqlOriginalError: err.originalError,
10
+ graphqlPath: err.path?.join('.'),
11
+ graphqlQuery: err.source?.body,
12
+ }, msg)
13
+ }
14
+
15
+
16
+ export function withErrorContext(ctx: object): (err: unknown) => never {
17
+ return function(err): never {
18
+ throw addErrorContext(err, ctx)
19
+ }
20
+ }
21
+
22
+
23
+ export function addErrorContext(error: unknown, ctx: object): Error {
24
+ let e = ensureError(error)
25
+ Object.assign(e, ctx)
26
+ return e
27
+ }
28
+
29
+
30
+ export function ensureError(err: unknown): Error {
31
+ if (err instanceof Error) return err
32
+ return new NonErrorThrown(err)
33
+ }
34
+
35
+
36
+ export class NonErrorThrown extends Error {
37
+ constructor(public readonly value: unknown) {
38
+ super('Non error object thrown')
39
+ }
40
+ }
@@ -0,0 +1,49 @@
1
+ interface Tx<T> {
2
+ close(): void
3
+ ctx: T
4
+ }
5
+
6
+
7
+ export class LazyTransaction<T> {
8
+ private closed = false
9
+
10
+ private tx?: Promise<Tx<T>>
11
+
12
+ constructor(private transact: (f: (ctx: T) => Promise<void>) => Promise<void>) {
13
+ }
14
+
15
+ async get(): Promise<T> {
16
+ if (this.closed) {
17
+ throw new Error("Too late to request transaction")
18
+ }
19
+ this.tx = this.tx || this.startTransaction()
20
+ let {ctx} = await this.tx
21
+ return ctx
22
+ }
23
+
24
+ private async startTransaction(): Promise<Tx<T>> {
25
+ return new Promise((resolve, reject) => {
26
+ let promise = this.transact(ctx => {
27
+ return new Promise<void>(close => {
28
+ resolve({
29
+ ctx,
30
+ close: () => {
31
+ close()
32
+ return promise
33
+ }
34
+ })
35
+ })
36
+ })
37
+ promise.catch(err => reject(err))
38
+ })
39
+ }
40
+
41
+ async close(): Promise<void> {
42
+ this.closed = true
43
+ if (this.tx) {
44
+ let tx = this.tx
45
+ this.tx = undefined
46
+ await tx.then(tx => tx.close())
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,65 @@
1
+ import {UserInputError} from "apollo-server-core"
2
+ import assert from "assert"
3
+ import {GraphQLResolveInfo, GraphQLSchema} from "graphql"
4
+ import {
5
+ FieldsByTypeName,
6
+ parseResolveInfo,
7
+ ResolveTree,
8
+ simplifyParsedResolveInfoFragmentWithType
9
+ } from "graphql-parse-resolve-info"
10
+
11
+
12
+ export type ResolveTreeFields = {
13
+ [alias: string]: ResolveTree
14
+ }
15
+
16
+
17
+ export interface ResolveTreeWithFields extends ResolveTree {
18
+ fields: ResolveTreeFields
19
+ }
20
+
21
+
22
+ export function simplifyResolveTree(schema: GraphQLSchema, tree: ResolveTree, typeName: string): ResolveTreeWithFields {
23
+ let type = schema.getType(typeName)
24
+ assert(type != null)
25
+ return simplifyParsedResolveInfoFragmentWithType(tree, type)
26
+ }
27
+
28
+
29
+ export function getResolveTree(info: GraphQLResolveInfo): ResolveTree
30
+ export function getResolveTree(info: GraphQLResolveInfo, typeName: string): ResolveTreeWithFields
31
+ export function getResolveTree(info: GraphQLResolveInfo, typeName?: string): ResolveTree {
32
+ let tree = parseResolveInfo(info)
33
+ assert(isResolveTree(tree))
34
+ if (typeName) {
35
+ return simplifyResolveTree(info.schema, tree, typeName)
36
+ } else {
37
+ return tree
38
+ }
39
+ }
40
+
41
+
42
+ function isResolveTree(resolveInfo: ResolveTree | FieldsByTypeName | null | undefined): resolveInfo is ResolveTree {
43
+ return resolveInfo != null && resolveInfo.fieldsByTypeName != null
44
+ }
45
+
46
+
47
+ export function getTreeRequest(treeFields: ResolveTreeFields, fieldName: string): ResolveTree | undefined {
48
+ let req: ResolveTree | undefined
49
+ for (let alias in treeFields) {
50
+ let e = treeFields[alias]
51
+ if (e.name != fieldName) continue
52
+ if (req != null) throw new UserInputError(`multiple aliases for field '${fieldName}' are not supported`)
53
+ req = e
54
+ }
55
+ return req
56
+ }
57
+
58
+
59
+ export function hasTreeRequest(treeFields: ResolveTreeFields, fieldName: string): boolean {
60
+ for (let alias in treeFields) {
61
+ let e = treeFields[alias]
62
+ if (e.name == fieldName) return true
63
+ }
64
+ return false
65
+ }