@subsquid/openreader 0.5.1 → 0.7.1

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 (117) hide show
  1. package/README.md +1 -1
  2. package/dist/dialect.d.ts +2 -0
  3. package/dist/dialect.d.ts.map +1 -0
  4. package/dist/dialect.js +3 -0
  5. package/dist/dialect.js.map +1 -0
  6. package/dist/gql/opencrud.d.ts +4 -3
  7. package/dist/gql/opencrud.d.ts.map +1 -1
  8. package/dist/gql/opencrud.js +12 -5
  9. package/dist/gql/opencrud.js.map +1 -1
  10. package/dist/gql/scalars/BigInt.d.ts +3 -0
  11. package/dist/gql/scalars/BigInt.d.ts.map +1 -0
  12. package/dist/gql/scalars/BigInt.js +36 -0
  13. package/dist/gql/scalars/BigInt.js.map +1 -0
  14. package/dist/gql/scalars/Bytes.d.ts +3 -0
  15. package/dist/gql/scalars/Bytes.d.ts.map +1 -0
  16. package/dist/gql/scalars/Bytes.js +32 -0
  17. package/dist/gql/scalars/Bytes.js.map +1 -0
  18. package/dist/gql/scalars/DateTime.d.ts +3 -0
  19. package/dist/gql/scalars/DateTime.d.ts.map +1 -0
  20. package/dist/gql/scalars/DateTime.js +44 -0
  21. package/dist/gql/scalars/DateTime.js.map +1 -0
  22. package/dist/gql/scalars/JSON.d.ts +3 -0
  23. package/dist/gql/scalars/JSON.d.ts.map +1 -0
  24. package/dist/gql/scalars/JSON.js +9 -0
  25. package/dist/gql/scalars/JSON.js.map +1 -0
  26. package/dist/gql/scalars/index.d.ts +7 -0
  27. package/dist/gql/scalars/index.d.ts.map +1 -0
  28. package/dist/gql/scalars/index.js +14 -0
  29. package/dist/gql/scalars/index.js.map +1 -0
  30. package/dist/gql/schema.d.ts.map +1 -1
  31. package/dist/gql/schema.js +3 -2
  32. package/dist/gql/schema.js.map +1 -1
  33. package/dist/orderBy.d.ts.map +1 -1
  34. package/dist/orderBy.js +4 -2
  35. package/dist/orderBy.js.map +1 -1
  36. package/dist/queryBuilder.d.ts +3 -1
  37. package/dist/queryBuilder.d.ts.map +1 -1
  38. package/dist/queryBuilder.js +121 -24
  39. package/dist/queryBuilder.js.map +1 -1
  40. package/dist/resolver.d.ts +2 -1
  41. package/dist/resolver.d.ts.map +1 -1
  42. package/dist/resolver.js +12 -12
  43. package/dist/resolver.js.map +1 -1
  44. package/dist/server.d.ts +2 -0
  45. package/dist/server.d.ts.map +1 -1
  46. package/dist/server.js +3 -2
  47. package/dist/server.js.map +1 -1
  48. package/dist/test/basic.test.js +3 -3
  49. package/dist/test/basic.test.js.map +1 -1
  50. package/dist/test/connection.test.js +1 -1
  51. package/dist/test/connection.test.js.map +1 -1
  52. package/dist/test/fts.test.js +2 -2
  53. package/dist/test/fts.test.js.map +1 -1
  54. package/dist/test/isNull.test.js +1 -1
  55. package/dist/test/isNull.test.js.map +1 -1
  56. package/dist/test/lists.test.js +3 -3
  57. package/dist/test/lists.test.js.map +1 -1
  58. package/dist/test/lookup.test.js +13 -7
  59. package/dist/test/lookup.test.js.map +1 -1
  60. package/dist/test/regressions.test.d.ts +2 -0
  61. package/dist/test/regressions.test.d.ts.map +1 -0
  62. package/dist/test/regressions.test.js +39 -0
  63. package/dist/test/regressions.test.js.map +1 -0
  64. package/dist/test/scalars.test.js +47 -10
  65. package/dist/test/scalars.test.js.map +1 -1
  66. package/dist/test/{util/setup.d.ts → setup.d.ts} +8 -1
  67. package/dist/test/setup.d.ts.map +1 -0
  68. package/dist/test/{util/setup.js → setup.js} +19 -8
  69. package/dist/test/setup.js.map +1 -0
  70. package/dist/test/typed-json.test.js +16 -2
  71. package/dist/test/typed-json.test.js.map +1 -1
  72. package/dist/test/unions.test.js +1 -1
  73. package/dist/test/unions.test.js.map +1 -1
  74. package/dist/test/where.test.js +1 -1
  75. package/dist/test/where.test.js.map +1 -1
  76. package/dist/util.d.ts +1 -0
  77. package/dist/util.d.ts.map +1 -1
  78. package/dist/util.js +5 -1
  79. package/dist/util.js.map +1 -1
  80. package/dist/where.d.ts +1 -1
  81. package/dist/where.d.ts.map +1 -1
  82. package/dist/where.js +5 -1
  83. package/dist/where.js.map +1 -1
  84. package/package.json +5 -5
  85. package/src/dialect.ts +2 -0
  86. package/src/gql/opencrud.ts +15 -6
  87. package/src/gql/scalars/BigInt.ts +34 -0
  88. package/src/gql/scalars/Bytes.ts +28 -0
  89. package/src/gql/scalars/DateTime.ts +45 -0
  90. package/src/gql/scalars/JSON.ts +7 -0
  91. package/src/gql/scalars/index.ts +12 -0
  92. package/src/gql/schema.ts +3 -2
  93. package/src/orderBy.ts +4 -2
  94. package/src/queryBuilder.ts +114 -20
  95. package/src/resolver.ts +13 -11
  96. package/src/server.ts +5 -2
  97. package/src/test/basic.test.ts +3 -3
  98. package/src/test/connection.test.ts +1 -1
  99. package/src/test/fts.test.ts +4 -2
  100. package/src/test/isNull.test.ts +1 -1
  101. package/src/test/lists.test.ts +3 -3
  102. package/src/test/lookup.test.ts +13 -7
  103. package/src/test/regressions.test.ts +39 -0
  104. package/src/test/scalars.test.ts +49 -10
  105. package/src/test/{util/setup.ts → setup.ts} +21 -7
  106. package/src/test/typed-json.test.ts +17 -2
  107. package/src/test/unions.test.ts +1 -1
  108. package/src/test/where.test.ts +1 -1
  109. package/src/util.ts +5 -0
  110. package/src/where.ts +9 -2
  111. package/dist/scalars.d.ts +0 -36
  112. package/dist/scalars.d.ts.map +0 -1
  113. package/dist/scalars.js +0 -229
  114. package/dist/scalars.js.map +0 -1
  115. package/dist/test/util/setup.d.ts.map +0 -1
  116. package/dist/test/util/setup.js.map +0 -1
  117. package/src/scalars.ts +0 -247
package/src/resolver.ts CHANGED
@@ -4,6 +4,8 @@ import {UserInputError} from "apollo-server-core"
4
4
  import assert from "assert"
5
5
  import type {GraphQLResolveInfo} from "graphql"
6
6
  import type {Database, Transaction} from "./db"
7
+ import type {Dialect} from "./dialect"
8
+ import {customScalars} from "./gql/scalars"
7
9
  import type {Entity, JsonObject, Model} from "./model"
8
10
  import {QueryBuilder} from "./queryBuilder"
9
11
  import {
@@ -15,7 +17,6 @@ import {
15
17
  PageInfo
16
18
  } from "./relayConnection"
17
19
  import {connectionRequestedFields, ftsRequestedFields, requestedFields} from "./requestedFields"
18
- import {getScalarResolvers} from "./scalars"
19
20
  import {ensureArray, toQueryListField, unsupportedCase} from "./util"
20
21
 
21
22
 
@@ -24,9 +25,9 @@ export interface ResolverContext {
24
25
  }
25
26
 
26
27
 
27
- export function buildResolvers(model: Model): IResolvers<unknown, ResolverContext> {
28
+ export function buildResolvers(model: Model, dialect: Dialect): IResolvers<unknown, ResolverContext> {
28
29
  let Query: Record<string, IFieldResolver<unknown, ResolverContext>> = {}
29
- let resolvers: IResolvers = {Query, ...getScalarResolvers()}
30
+ let resolvers: IResolvers = {Query, ...customScalars}
30
31
 
31
32
  for (let name in model) {
32
33
  let item = model[name]
@@ -35,16 +36,16 @@ export function buildResolvers(model: Model): IResolvers<unknown, ResolverContex
35
36
  Query[toQueryListField(name)] = async (source, args, context, info) => {
36
37
  let fields = requestedFields(model, name, info)
37
38
  let db = await context.openReaderTransaction.get()
38
- return new QueryBuilder(model, db).executeSelect(name, args, fields)
39
+ return new QueryBuilder(model, dialect, db).executeSelect(name, args, fields)
39
40
  }
40
41
  Query[toQueryListField(name) + 'Connection'] = async (source, args, context, info) => {
41
42
  let db = await context.openReaderTransaction.get()
42
- return resolveEntityConnection(model, name, args, info, db)
43
+ return resolveEntityConnection(model, dialect, name, args, info, db)
43
44
  }
44
45
  Query[`${toCamelCase(name)}ById`] = async (source, args, context, info) => {
45
46
  let fields = requestedFields(model, name, info)
46
47
  let db = await context.openReaderTransaction.get()
47
- let result = await new QueryBuilder(model, db)
48
+ let result = await new QueryBuilder(model, dialect, db)
48
49
  .executeSelect(name, {where: {id_eq: args.id}}, fields)
49
50
  assert(result.length < 2)
50
51
  return result[0]
@@ -52,7 +53,7 @@ export function buildResolvers(model: Model): IResolvers<unknown, ResolverContex
52
53
  Query[`${toCamelCase(name)}ByUniqueInput`] = async (source, args, context, info) => {
53
54
  let fields = requestedFields(model, name, info)
54
55
  let db = await context.openReaderTransaction.get()
55
- let result = await new QueryBuilder(model, db)
56
+ let result = await new QueryBuilder(model, dialect, db)
56
57
  .executeSelect(name, {where: {id_eq: args.where.id}}, fields)
57
58
  assert(result.length < 2)
58
59
  return result[0]
@@ -71,7 +72,7 @@ export function buildResolvers(model: Model): IResolvers<unknown, ResolverContex
71
72
  Query[name] = async (source, args, context, info) => {
72
73
  let fields = ftsRequestedFields(model, name, info)
73
74
  let db = await context.openReaderTransaction.get()
74
- return new QueryBuilder(model, db).executeFulltextSearch(name, args, fields)
75
+ return new QueryBuilder(model, dialect, db).executeFulltextSearch(name, args, fields)
75
76
  }
76
77
  resolvers[`${name}_Item`] = {
77
78
  __resolveType: resolveUnionType
@@ -130,6 +131,7 @@ interface ConnectionResponse extends RelayConnectionResponse<any> {
130
131
 
131
132
  async function resolveEntityConnection(
132
133
  model: Model,
134
+ dialect: Dialect,
133
135
  entityName: string,
134
136
  args: ConnectionArgs,
135
137
  info: GraphQLResolveInfo,
@@ -162,7 +164,7 @@ async function resolveEntityConnection(
162
164
 
163
165
  let fields = connectionRequestedFields(model, entityName, info)
164
166
  if (fields.edges?.node) {
165
- let nodes = await new QueryBuilder(model, db).executeSelect(entityName, listArgs, fields.edges.node)
167
+ let nodes = await new QueryBuilder(model, dialect, db).executeSelect(entityName, listArgs, fields.edges.node)
166
168
  let edges: ConnectionEdge<any>[] = new Array(Math.min(limit, nodes.length))
167
169
  for (let i = 0; i < edges.length; i++) {
168
170
  edges[i] = {
@@ -176,7 +178,7 @@ async function resolveEntityConnection(
176
178
  response.totalCount = offset + nodes.length
177
179
  }
178
180
  } else if (fields.edges?.cursor || fields.pageInfo) {
179
- let listLength = await new QueryBuilder(model, db).executeListCount(entityName, listArgs)
181
+ let listLength = await new QueryBuilder(model, dialect, db).executeListCount(entityName, listArgs)
180
182
  response.pageInfo = pageInfo(listLength)
181
183
  if (fields.edges?.cursor) {
182
184
  response.edges = []
@@ -192,7 +194,7 @@ async function resolveEntityConnection(
192
194
  }
193
195
 
194
196
  if (fields.totalCount && response.totalCount == null) {
195
- response.totalCount = await new QueryBuilder(model, db).executeSelectCount(entityName, listArgs.where)
197
+ response.totalCount = await new QueryBuilder(model, dialect, db).executeSelectCount(entityName, listArgs.where)
196
198
  }
197
199
 
198
200
  return response
package/src/server.ts CHANGED
@@ -7,6 +7,7 @@ import http from "http"
7
7
  import path from "path"
8
8
  import type {Pool} from "pg"
9
9
  import {PoolTransaction} from "./db"
10
+ import {Dialect} from "./dialect"
10
11
  import {buildServerSchema} from "./gql/opencrud"
11
12
  import type {Model} from "./model"
12
13
  import {buildResolvers} from "./resolver"
@@ -22,14 +23,16 @@ export interface ServerOptions {
22
23
  model: Model
23
24
  db: Pool
24
25
  port: number | string
26
+ dialect?: Dialect
25
27
  graphiqlConsole?: boolean
26
28
  }
27
29
 
28
30
 
29
31
  export async function serve(options: ServerOptions): Promise<ListeningServer> {
30
32
  let {model, db} = options
31
- let resolvers = buildResolvers(model)
32
- let typeDefs = buildServerSchema(model)
33
+ let dialect = options.dialect ?? 'postgres'
34
+ let resolvers = buildResolvers(model, dialect)
35
+ let typeDefs = buildServerSchema(model, dialect)
33
36
  let app = express()
34
37
  let server = http.createServer(app)
35
38
 
@@ -1,4 +1,4 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {useDatabase, useServer} from "./setup"
2
2
 
3
3
 
4
4
  describe('basic tests', function() {
@@ -44,11 +44,11 @@ describe('basic tests', function() {
44
44
  it('can fetch all accounts', function() {
45
45
  return client.test(
46
46
  `query {
47
- accounts {
47
+ accounts(orderBy: id_ASC) {
48
48
  id
49
49
  wallet
50
50
  balance
51
- history { balance }
51
+ history(orderBy: id_ASC) { balance }
52
52
  }
53
53
  }`,
54
54
  {
@@ -1,4 +1,4 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {useDatabase, useServer} from "./setup"
2
2
 
3
3
  describe('relay connections', function () {
4
4
  useDatabase([
@@ -1,10 +1,12 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {isCockroach, useDatabase, useServer} from "./setup"
2
+
2
3
 
3
4
  function tsvector(columns: string[]) {
4
5
  return columns.map(col => `setweight(to_tsvector('english', coalesce(${col}, '')), 'A')`).join(' || ')
5
6
  }
6
7
 
7
- describe('full text search', function () {
8
+
9
+ isCockroach() || describe('full text search', function () {
8
10
  useDatabase([
9
11
  `create table foo (
10
12
  id text primary key,
@@ -1,4 +1,4 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {useDatabase, useServer} from "./setup"
2
2
 
3
3
 
4
4
  describe('isNull operator', function() {
@@ -1,4 +1,4 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {useDatabase, useServer} from "./setup"
2
2
 
3
3
 
4
4
  describe('lists', function () {
@@ -172,8 +172,8 @@ describe('lists', function () {
172
172
  `, {
173
173
  lists: [{
174
174
  datetimeArray: [
175
- '2020-01-01T00:00:00.000Z',
176
- '2021-01-01T00:00:00.000Z'
175
+ '2020-01-01T00:00:00.000000Z',
176
+ '2021-01-01T00:00:00.000000Z'
177
177
  ]
178
178
  }]
179
179
  })
@@ -1,4 +1,4 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {isCockroach, useDatabase, useServer} from "./setup"
2
2
 
3
3
  describe('lookup test', function () {
4
4
  useDatabase([
@@ -82,16 +82,22 @@ describe('lookup test', function () {
82
82
  it('supports sorting on lookup fields', function () {
83
83
  return client.test(`
84
84
  query {
85
- issues(orderBy: [payment_amount_DESC]) {
85
+ issues(orderBy: [payment_amount_ASC]) {
86
86
  id
87
87
  }
88
88
  }
89
89
  `, {
90
- issues: [
91
- {id: '3'},
92
- {id: '1'},
93
- {id: '2'}
94
- ]
90
+ issues: isCockroach()
91
+ ? [
92
+ {id: '3'},
93
+ {id: '2'},
94
+ {id: '1'}
95
+ ]
96
+ : [
97
+ {id: '2'},
98
+ {id: '1'},
99
+ {id: '3'}
100
+ ]
95
101
  })
96
102
  })
97
103
 
@@ -0,0 +1,39 @@
1
+ import {useDatabase, useServer} from "./setup"
2
+
3
+ describe('regressions', function () {
4
+ describe('empty lookup lists', function () {
5
+ useDatabase([
6
+ `create table "order" (id text primary key)`,
7
+ `create table item (id text primary key, order_id text)`,
8
+ `insert into "order" (id) values ('1')`
9
+ ])
10
+
11
+ const client = useServer(`
12
+ type Order @entity {
13
+ id: ID!
14
+ items: [Item!]! @derivedFrom(field: "order")
15
+ }
16
+
17
+ type Item @entity {
18
+ id: ID!
19
+ order: Order
20
+ }
21
+ `)
22
+
23
+ it('should return empty array for empty lookup list', async function () {
24
+ return client.test(`
25
+ query {
26
+ orders {
27
+ id
28
+ items { id }
29
+ }
30
+ }
31
+ `, {
32
+ orders: [{
33
+ id: '1',
34
+ items: []
35
+ }]
36
+ })
37
+ })
38
+ })
39
+ })
@@ -1,8 +1,8 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {useDatabase, useServer} from "./setup"
2
2
 
3
3
  describe('scalars', function() {
4
4
  useDatabase([
5
- `create table scalar (id text primary key, "boolean" bool, "bigint" numeric, "string" text, enum text, date_time timestamptz, "bytes" bytea, deep jsonb)`,
5
+ `create table scalar (id text primary key, "boolean" bool, "bigint" numeric, "string" text, enum text, date_time timestamptz, "bytes" bytea, "json" jsonb, deep jsonb)`,
6
6
  `insert into scalar (id, "boolean") values ('1', true)`,
7
7
  `insert into scalar (id, "boolean", deep) values ('2', false, '{"boolean": true}'::jsonb)`,
8
8
  `insert into scalar (id, "bigint", deep) values ('3', 1000000000000000000000000000000000000, '{"bigint": "1000000000000000000000000000000000000"}'::jsonb)`,
@@ -12,6 +12,7 @@ describe('scalars', function() {
12
12
  `insert into scalar (id, "string") values ('7', 'bar baz foo')`,
13
13
  `insert into scalar (id, "string") values ('8', 'baz foo bar')`,
14
14
  `insert into scalar (id, "string") values ('9', 'hello')`,
15
+ `insert into scalar (id, "string") values ('9-1', 'A fOo B')`,
15
16
  `insert into scalar (id, "date_time", deep) values ('10', '2021-09-24T15:43:13.400Z', '{"dateTime": "2021-09-24T00:00:00.120Z"}'::jsonb)`,
16
17
  `insert into scalar (id, "date_time", deep) values ('11', '2021-09-24T00:00:00.000Z', '{"dateTime": "2021-09-24T00:00:00Z"}'::jsonb)`,
17
18
  `insert into scalar (id, "date_time", deep) values ('12', '2021-09-24 02:00:00.001 +01:00', '{"dateTime": "2021-09-24T00:00:00.1Z"}'::jsonb)`,
@@ -20,6 +21,8 @@ describe('scalars', function() {
20
21
  `insert into scalar (id, "enum") values ('15', 'A')`,
21
22
  `insert into scalar (id, "enum") values ('16', 'B')`,
22
23
  `insert into scalar (id, "enum") values ('17', 'C')`,
24
+ `insert into scalar (id, "json") values ('18', '{"key1": "value1"}'::jsonb)`,
25
+ `insert into scalar (id, "json") values ('19', '{"key2": "value2"}'::jsonb)`,
23
26
  ])
24
27
 
25
28
  const client = useServer(`
@@ -31,6 +34,7 @@ describe('scalars', function() {
31
34
  bigint: BigInt
32
35
  dateTime: DateTime
33
36
  bytes: Bytes
37
+ json: JSON,
34
38
  deep: DeepScalar
35
39
  }
36
40
 
@@ -106,16 +110,18 @@ describe('scalars', function() {
106
110
  not_ends_with: scalars(where: {string_not_endsWith: "foo"} orderBy: id_ASC) { id }
107
111
  contains: scalars(where: {string_contains: "foo"} orderBy: id_ASC) { id }
108
112
  not_contains: scalars(where: {string_not_contains: "foo"} orderBy: id_ASC) { id }
109
- case_sensitive: scalars(where: {string_contains: "Foo"} orderBy: id_ASC) { id }
113
+ contains_insensitive: scalars(where: {string_containsInsensitive: "FoO"} orderBy: id_ASC) { id }
114
+ not_contains_insensitive: scalars(where: {string_not_containsInsensitive: "FoO"} orderBy: id_ASC) { id }
110
115
  }
111
116
  `, {
112
117
  starts_with: [{id: '6'}],
113
- not_starts_with: [{id: '7'}, {id: '8'}, {id: '9'}],
118
+ not_starts_with: [{id: '7'}, {id: '8'}, {id: '9'}, {id: '9-1'}],
114
119
  ends_with: [{id: '7'}],
115
- not_ends_with: [{id: '6'}, {id: '8'}, {id: '9'}],
120
+ not_ends_with: [{id: '6'}, {id: '8'}, {id: '9'}, {id: '9-1'}],
116
121
  contains: [{id: '6'}, {id: '7'}, {id: '8'}],
117
- not_contains: [{id: '9'}],
118
- case_sensitive: []
122
+ not_contains: [{id: '9'}, {id: '9-1'}],
123
+ contains_insensitive: [{id: '6'}, {id: '7'}, {id: '8'}, {id: '9-1'}],
124
+ not_contains_insensitive: [{id: '9'}]
119
125
  })
120
126
  })
121
127
  })
@@ -239,9 +245,9 @@ describe('scalars', function() {
239
245
  }
240
246
  `, {
241
247
  scalars: [
242
- {id: '10', dateTime: '2021-09-24T15:43:13.400Z', deep: {dateTime: '2021-09-24T00:00:00.120Z'}},
243
- {id: '11', dateTime: '2021-09-24T00:00:00.000Z', deep: {dateTime: '2021-09-24T00:00:00Z'}},
244
- {id: '12', dateTime: '2021-09-24T01:00:00.001Z', deep: {dateTime: '2021-09-24T00:00:00.1Z'}}
248
+ {id: '10', dateTime: '2021-09-24T15:43:13.400000Z', deep: {dateTime: '2021-09-24T00:00:00.120Z'}},
249
+ {id: '11', dateTime: '2021-09-24T00:00:00.000000Z', deep: {dateTime: '2021-09-24T00:00:00Z'}},
250
+ {id: '12', dateTime: '2021-09-24T01:00:00.001000Z', deep: {dateTime: '2021-09-24T00:00:00.1Z'}}
245
251
  ]
246
252
  })
247
253
  })
@@ -313,4 +319,37 @@ describe('scalars', function() {
313
319
  })
314
320
  })
315
321
  })
322
+
323
+ describe('JSON', function () {
324
+ it('outputs correctly', function() {
325
+ return client.test(`
326
+ query {
327
+ scalars(where: {id_in: ["18"]}, orderBy: id_ASC) {
328
+ id
329
+ json
330
+ }
331
+ }
332
+ `, {
333
+ scalars: [
334
+ {id: '18', json: {'key1': 'value1'}},
335
+ ]
336
+ })
337
+ })
338
+
339
+ it('supports where conditions', function () {
340
+ return client.test(`
341
+ query {
342
+ eq: scalars(where: {json_eq: {key1: "value1"}}) { id }
343
+ jsonHasKey: scalars(where: {json_jsonHasKey: "key1"}) { id }
344
+ jsonContains: scalars(where: {json_jsonContains: {key2: "value2"}}) { id }
345
+ missingKey: scalars(where: {json_jsonHasKey: {foo: 1}}) { id }
346
+ }
347
+ `, {
348
+ eq: [{id: '18'}],
349
+ jsonHasKey: [{id: '18'}],
350
+ jsonContains: [{id: '19'}],
351
+ missingKey: []
352
+ })
353
+ })
354
+ })
316
355
  })
@@ -1,12 +1,25 @@
1
+ import {assertNotNull} from "@subsquid/util"
1
2
  import {Client} from "gql-test-client"
2
3
  import {parse} from "graphql"
3
4
  import {Client as PgClient, ClientBase, Pool} from "pg"
4
- import {createPoolConfig} from "../../db"
5
- import {buildModel, buildSchema} from "../../gql/schema"
6
- import {ListeningServer, serve} from "../../server"
5
+ import {buildModel, buildSchema} from "../gql/schema"
6
+ import {ListeningServer, serve} from "../server"
7
7
 
8
8
 
9
- export const db_config = createPoolConfig()
9
+ export function isCockroach(): boolean {
10
+ return process.env.DB_TYPE == 'cockroach'
11
+ }
12
+
13
+
14
+ export const db_config = {
15
+ host: 'localhost',
16
+ port: parseInt(assertNotNull(
17
+ isCockroach() ? process.env.DB_PORT_COCKROACH : process.env.DB_PORT_PG
18
+ )),
19
+ user: 'root',
20
+ password: 'root',
21
+ database: 'defaultdb'
22
+ }
10
23
 
11
24
 
12
25
  async function withClient(block: (client: ClientBase) => Promise<void>): Promise<void> {
@@ -31,8 +44,8 @@ export function databaseInit(sql: string[]): Promise<void> {
31
44
 
32
45
  export function databaseDelete(): Promise<void> {
33
46
  return withClient(async client => {
34
- await client.query(`DROP SCHEMA IF EXISTS public CASCADE`)
35
- await client.query(`CREATE SCHEMA public`)
47
+ await client.query(`DROP SCHEMA IF EXISTS root CASCADE`)
48
+ await client.query(`CREATE SCHEMA root`)
36
49
  })
37
50
  }
38
51
 
@@ -53,7 +66,8 @@ export function useServer(schema: string): Client {
53
66
  info = await serve({
54
67
  db,
55
68
  model: buildModel(buildSchema(parse(schema))),
56
- port: 0
69
+ port: 0,
70
+ dialect: isCockroach() ? 'cockroach' : 'postgres'
57
71
  })
58
72
  client.endpoint = `http://localhost:${info.port}/graphql`
59
73
  })
@@ -1,9 +1,9 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {useDatabase, useServer} from "./setup"
2
2
 
3
3
  describe('typed json fields', function () {
4
4
  useDatabase([
5
5
  `create table entity (id text primary key, a jsonb)`,
6
- `insert into entity (id, a) values ('1', '{"a": "a", "b": {"b": "b", "e": "1"}}'::jsonb)`,
6
+ `insert into entity (id, a) values ('1', '{"a": "a", "b": {"b": "b", "e": "1"}, "c": [1, 2, 3]}'::jsonb)`,
7
7
  `insert into entity (id, a) values ('2', '{"a": "A", "b": {"b": "B", "e": "1"}}'::jsonb)`,
8
8
  `insert into entity (id, a) values ('3', '{}'::jsonb)`,
9
9
  `insert into entity (id) values ('4')`,
@@ -17,6 +17,7 @@ describe('typed json fields', function () {
17
17
  type A {
18
18
  a: String
19
19
  b: B
20
+ c: JSON
20
21
  }
21
22
 
22
23
  type B {
@@ -44,6 +45,20 @@ describe('typed json fields', function () {
44
45
  })
45
46
  })
46
47
 
48
+ it('nested JSON scalar', function () {
49
+ return client.test(`
50
+ query {
51
+ entities(where: {id_eq: "1"}) {
52
+ a { c }
53
+ }
54
+ }
55
+ `, {
56
+ entities: [{
57
+ a: {c: [1, 2, 3]}
58
+ }]
59
+ })
60
+ })
61
+
47
62
  it('can fetch deep entities', function () {
48
63
  return client.test(`
49
64
  query {
@@ -1,4 +1,4 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {useDatabase, useServer} from "./setup"
2
2
 
3
3
  describe('unions', function () {
4
4
  useDatabase([
@@ -1,4 +1,4 @@
1
- import {useDatabase, useServer} from "./util/setup"
1
+ import {useDatabase, useServer} from "./setup"
2
2
 
3
3
  describe('AND, OR on entity filters', function () {
4
4
  useDatabase([
package/src/util.ts CHANGED
@@ -37,3 +37,8 @@ export function toInt(val: number | string): number {
37
37
  assert(!isNaN(i) && isFinite(i))
38
38
  return i
39
39
  }
40
+
41
+
42
+ export function invalidFormat(type: string, value: string): Error {
43
+ return new TypeError(`Not a ${type}: ${value}`)
44
+ }
package/src/where.ts CHANGED
@@ -9,6 +9,7 @@ export type WhereOp =
9
9
  'lte' |
10
10
  'in' | 'not_in' |
11
11
  'contains' | 'not_contains' |
12
+ 'containsInsensitive' | 'not_containsInsensitive' |
12
13
  'startsWith' | 'not_startsWith' |
13
14
  'endsWith' | 'not_endsWith' |
14
15
  'containsAll' |
@@ -16,7 +17,9 @@ export type WhereOp =
16
17
  'containsNone' |
17
18
  'some' |
18
19
  'every' |
19
- 'none'
20
+ 'none' |
21
+ 'jsonContains' |
22
+ 'jsonHasKey'
20
23
 
21
24
 
22
25
  const ENDINGS = [
@@ -31,6 +34,8 @@ const ENDINGS = [
31
34
  'not_in',
32
35
  'contains',
33
36
  'not_contains',
37
+ 'containsInsensitive',
38
+ 'not_containsInsensitive',
34
39
  'startsWith',
35
40
  'not_startsWith',
36
41
  'endsWith',
@@ -40,7 +45,9 @@ const ENDINGS = [
40
45
  'containsNone',
41
46
  'some',
42
47
  'every',
43
- 'none'
48
+ 'none',
49
+ 'jsonContains',
50
+ 'jsonHasKey',
44
51
  ].sort((a, b) => b.length - a.length).map(e => '_' + e)
45
52
 
46
53
 
package/dist/scalars.d.ts DELETED
@@ -1,36 +0,0 @@
1
- /**
2
- * The current concept of custom scalars is as follows:
3
- *
4
- * Each custom scalar has a canonical string representation which is used almost everywhere:
5
- * in JSON responses
6
- * in graphql queries/schemas
7
- * in jsonb database columns
8
- * in database results
9
- *
10
- * Database must support 2 way coercion between underlying database type and canonical representation
11
- * of a corresponding scalar.
12
- *
13
- * We receive from database canonical strings and use them within our resolvers as is.
14
- *
15
- * GraphQL parsing procedures convert canonical string representation to corresponding js type.
16
- * This is for compatibility with possible extensions which would like to reuse our scalars.
17
- *
18
- * In GraphQL serialization procedures we accept both a canonical string representation
19
- * and corresponding js type.
20
- */
21
- import { GraphQLScalarType } from "graphql";
22
- export interface Scalar {
23
- gql: GraphQLScalarType;
24
- fromStringCast: (sqlExp: string) => string;
25
- toStringCast: (sqlExp: string) => string;
26
- toStringArrayCast: (sqlExp: string) => string;
27
- }
28
- export declare const scalars: Record<string, Scalar>;
29
- export declare const scalars_list: string[];
30
- export declare function getScalarResolvers(): Record<string, GraphQLScalarType>;
31
- export declare function toOutputCast(scalarType: string, sqlExp: string): string;
32
- export declare function fromStringCast(scalarType: string, sqlExp: string): string;
33
- export declare function toOutputArrayCast(scalarType: string, sqlExp: string): string;
34
- export declare function fromJsonCast(scalarType: string, objSqlExp: string, prop: string): string;
35
- export declare function fromJsonToOutputCast(scalarType: string, objSqlExp: string, prop: string): string;
36
- //# sourceMappingURL=scalars.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scalars.d.ts","sourceRoot":"","sources":["../src/scalars.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAA;AAGzC,MAAM,WAAW,MAAM;IACnB,GAAG,EAAE,iBAAiB,CAAA;IACtB,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;IACxC,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;CAChD;AAGD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA0G1C,CAAA;AAyCD,eAAO,MAAM,YAAY,UAAsC,CAAA;AAG/D,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAMtE;AAGD,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOvE;AAGD,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOzE;AAGD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO5E;AAGD,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAWxF;AAGD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAWvF"}