@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.
Files changed (295) hide show
  1. package/bin/main.js +1 -1
  2. package/lib/context.d.ts +14 -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 +8 -0
  27. package/lib/limit.size.d.ts.map +1 -0
  28. package/lib/limit.size.js +107 -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 +59 -0
  33. package/lib/main.js.map +1 -0
  34. package/{dist → lib}/model.d.ts +11 -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} +62 -8
  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 +527 -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 +42 -0
  83. package/lib/server.d.ts.map +1 -0
  84. package/lib/server.js +171 -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/lib/test/limits.test.d.ts +2 -0
  127. package/lib/test/limits.test.d.ts.map +1 -0
  128. package/lib/test/limits.test.js +159 -0
  129. package/lib/test/limits.test.js.map +1 -0
  130. package/{dist → lib}/test/lists.test.d.ts +0 -0
  131. package/{dist → lib}/test/lists.test.d.ts.map +0 -0
  132. package/{dist → lib}/test/lists.test.js +0 -0
  133. package/{dist → lib}/test/lists.test.js.map +0 -0
  134. package/{dist → lib}/test/lookup.test.d.ts +0 -0
  135. package/{dist → lib}/test/lookup.test.d.ts.map +0 -0
  136. package/{dist → lib}/test/lookup.test.js +0 -0
  137. package/{dist → lib}/test/lookup.test.js.map +0 -0
  138. package/{dist → lib}/test/regressions.test.d.ts +0 -0
  139. package/{dist → lib}/test/regressions.test.d.ts.map +0 -0
  140. package/{dist → lib}/test/regressions.test.js +0 -0
  141. package/{dist → lib}/test/regressions.test.js.map +0 -0
  142. package/{dist → lib}/test/scalars.test.d.ts +0 -0
  143. package/{dist → lib}/test/scalars.test.d.ts.map +0 -0
  144. package/{dist → lib}/test/scalars.test.js +0 -0
  145. package/{dist → lib}/test/scalars.test.js.map +0 -0
  146. package/lib/test/setup.d.ts +17 -0
  147. package/lib/test/setup.d.ts.map +1 -0
  148. package/{dist → lib}/test/setup.js +18 -13
  149. package/lib/test/setup.js.map +1 -0
  150. package/lib/test/subscription.test.d.ts +2 -0
  151. package/lib/test/subscription.test.d.ts.map +1 -0
  152. package/lib/test/subscription.test.js +99 -0
  153. package/lib/test/subscription.test.js.map +1 -0
  154. package/{dist → lib}/test/tools.test.d.ts +0 -0
  155. package/{dist → lib}/test/tools.test.d.ts.map +0 -0
  156. package/{dist → lib}/test/tools.test.js +0 -0
  157. package/{dist → lib}/test/tools.test.js.map +0 -0
  158. package/{dist → lib}/test/typed-json.test.d.ts +0 -0
  159. package/{dist → lib}/test/typed-json.test.d.ts.map +0 -0
  160. package/{dist → lib}/test/typed-json.test.js +0 -0
  161. package/{dist → lib}/test/typed-json.test.js.map +0 -0
  162. package/{dist → lib}/test/unions.test.d.ts +0 -0
  163. package/{dist → lib}/test/unions.test.d.ts.map +0 -0
  164. package/{dist → lib}/test/unions.test.js +0 -0
  165. package/{dist → lib}/test/unions.test.js.map +0 -0
  166. package/{dist → lib}/test/where.test.d.ts +0 -0
  167. package/{dist → lib}/test/where.test.d.ts.map +0 -0
  168. package/{dist → lib}/test/where.test.js +0 -0
  169. package/{dist → lib}/test/where.test.js.map +0 -0
  170. package/{dist → lib}/tools.d.ts +0 -0
  171. package/{dist → lib}/tools.d.ts.map +0 -0
  172. package/{dist → lib}/tools.js +3 -3
  173. package/{dist → lib}/tools.js.map +1 -1
  174. package/lib/util/error-handling.d.ts +11 -0
  175. package/lib/util/error-handling.d.ts.map +1 -0
  176. package/lib/util/error-handling.js +42 -0
  177. package/lib/util/error-handling.js.map +1 -0
  178. package/lib/util/execute.d.ts +5 -0
  179. package/lib/util/execute.d.ts.map +1 -0
  180. package/lib/util/execute.js +28 -0
  181. package/lib/util/execute.js.map +1 -0
  182. package/lib/util/lazy-transaction.d.ts +10 -0
  183. package/lib/util/lazy-transaction.d.ts.map +1 -0
  184. package/lib/util/lazy-transaction.js +43 -0
  185. package/lib/util/lazy-transaction.js.map +1 -0
  186. package/lib/util/limit.d.ts +11 -0
  187. package/lib/util/limit.d.ts.map +1 -0
  188. package/lib/util/limit.js +39 -0
  189. package/lib/util/limit.js.map +1 -0
  190. package/lib/util/resolve-tree.d.ts +14 -0
  191. package/lib/util/resolve-tree.d.ts.map +1 -0
  192. package/lib/util/resolve-tree.js +52 -0
  193. package/lib/util/resolve-tree.js.map +1 -0
  194. package/{dist → lib/util}/util.d.ts +2 -3
  195. package/lib/util/util.d.ts.map +1 -0
  196. package/{dist → lib/util}/util.js +9 -13
  197. package/lib/util/util.js.map +1 -0
  198. package/package.json +18 -10
  199. package/src/context.ts +17 -0
  200. package/src/db.ts +46 -57
  201. package/src/ir/args.ts +85 -0
  202. package/src/ir/connection.ts +48 -0
  203. package/src/ir/fields.ts +40 -0
  204. package/src/limit.size.ts +130 -0
  205. package/src/main.ts +74 -42
  206. package/src/{gql/schema.ts → model.schema.ts} +72 -9
  207. package/src/model.ts +13 -1
  208. package/src/{orderBy.ts → opencrud/orderBy.ts} +3 -10
  209. package/src/opencrud/schema.ts +639 -0
  210. package/src/opencrud/tree.ts +144 -0
  211. package/src/opencrud/where.ts +141 -0
  212. package/src/{gql/scalars → scalars}/BigInt.ts +1 -1
  213. package/src/{gql/scalars → scalars}/Bytes.ts +4 -4
  214. package/src/{gql/scalars → scalars}/DateTime.ts +1 -1
  215. package/src/{gql/scalars → scalars}/JSON.ts +0 -0
  216. package/src/{gql/scalars → scalars}/index.ts +0 -0
  217. package/src/server.ts +175 -55
  218. package/src/sql/cursor.ts +291 -0
  219. package/src/sql/mapping.ts +66 -0
  220. package/src/sql/printer.ts +328 -0
  221. package/src/sql/query.ts +194 -0
  222. package/src/sql/util.ts +89 -0
  223. package/src/subscription.ts +46 -0
  224. package/src/test/fts.test.ts +1 -1
  225. package/src/test/limits.test.ts +163 -0
  226. package/src/test/setup.ts +16 -11
  227. package/src/test/subscription.test.ts +98 -0
  228. package/src/tools.ts +1 -1
  229. package/src/util/error-handling.ts +40 -0
  230. package/src/util/execute.ts +53 -0
  231. package/src/util/lazy-transaction.ts +49 -0
  232. package/src/util/limit.ts +34 -0
  233. package/src/util/resolve-tree.ts +65 -0
  234. package/src/{util.ts → util/util.ts} +9 -14
  235. package/dist/db.d.ts +0 -28
  236. package/dist/db.d.ts.map +0 -1
  237. package/dist/db.js +0 -69
  238. package/dist/db.js.map +0 -1
  239. package/dist/gql/opencrud.d.ts +0 -6
  240. package/dist/gql/opencrud.d.ts.map +0 -1
  241. package/dist/gql/opencrud.js +0 -326
  242. package/dist/gql/opencrud.js.map +0 -1
  243. package/dist/gql/scalars/BigInt.d.ts.map +0 -1
  244. package/dist/gql/scalars/BigInt.js.map +0 -1
  245. package/dist/gql/scalars/Bytes.d.ts.map +0 -1
  246. package/dist/gql/scalars/Bytes.js.map +0 -1
  247. package/dist/gql/scalars/DateTime.d.ts.map +0 -1
  248. package/dist/gql/scalars/DateTime.js.map +0 -1
  249. package/dist/gql/scalars/JSON.d.ts.map +0 -1
  250. package/dist/gql/scalars/JSON.js.map +0 -1
  251. package/dist/gql/scalars/index.d.ts.map +0 -1
  252. package/dist/gql/scalars/index.js.map +0 -1
  253. package/dist/gql/schema.d.ts.map +0 -1
  254. package/dist/gql/schema.js.map +0 -1
  255. package/dist/main.js +0 -44
  256. package/dist/main.js.map +0 -1
  257. package/dist/model.d.ts.map +0 -1
  258. package/dist/orderBy.d.ts.map +0 -1
  259. package/dist/orderBy.js.map +0 -1
  260. package/dist/queryBuilder.d.ts +0 -56
  261. package/dist/queryBuilder.d.ts.map +0 -1
  262. package/dist/queryBuilder.js +0 -733
  263. package/dist/queryBuilder.js.map +0 -1
  264. package/dist/relayConnection.d.ts +0 -37
  265. package/dist/relayConnection.d.ts.map +0 -1
  266. package/dist/relayConnection.js +0 -43
  267. package/dist/relayConnection.js.map +0 -1
  268. package/dist/requestedFields.d.ts +0 -33
  269. package/dist/requestedFields.d.ts.map +0 -1
  270. package/dist/requestedFields.js +0 -179
  271. package/dist/requestedFields.js.map +0 -1
  272. package/dist/resolver.d.ts +0 -9
  273. package/dist/resolver.d.ts.map +0 -1
  274. package/dist/resolver.js +0 -158
  275. package/dist/resolver.js.map +0 -1
  276. package/dist/server.d.ts +0 -22
  277. package/dist/server.d.ts.map +0 -1
  278. package/dist/server.js +0 -96
  279. package/dist/server.js.map +0 -1
  280. package/dist/test/fts.test.js.map +0 -1
  281. package/dist/test/setup.d.ts +0 -14
  282. package/dist/test/setup.d.ts.map +0 -1
  283. package/dist/test/setup.js.map +0 -1
  284. package/dist/util.d.ts.map +0 -1
  285. package/dist/util.js.map +0 -1
  286. package/dist/where.d.ts +0 -9
  287. package/dist/where.d.ts.map +0 -1
  288. package/dist/where.js +0 -101
  289. package/dist/where.js.map +0 -1
  290. package/src/gql/opencrud.ts +0 -350
  291. package/src/queryBuilder.ts +0 -891
  292. package/src/relayConnection.ts +0 -80
  293. package/src/requestedFields.ts +0 -246
  294. package/src/resolver.ts +0 -201
  295. 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,
@@ -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 "../gql/schema"
6
- import {ListeningServer, serve} from "../server"
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 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,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 databaseInit(sql)
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?.stop())
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 "./gql/schema"
6
+ import {buildModel, buildSchema} from "./model.schema"
7
7
  import type {Model} from "./model"
8
8
 
9
9