@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.
- package/README.md +1 -1
- package/dist/dialect.d.ts +2 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +3 -0
- package/dist/dialect.js.map +1 -0
- package/dist/gql/opencrud.d.ts +4 -3
- package/dist/gql/opencrud.d.ts.map +1 -1
- package/dist/gql/opencrud.js +12 -5
- package/dist/gql/opencrud.js.map +1 -1
- package/dist/gql/scalars/BigInt.d.ts +3 -0
- package/dist/gql/scalars/BigInt.d.ts.map +1 -0
- package/dist/gql/scalars/BigInt.js +36 -0
- package/dist/gql/scalars/BigInt.js.map +1 -0
- package/dist/gql/scalars/Bytes.d.ts +3 -0
- package/dist/gql/scalars/Bytes.d.ts.map +1 -0
- package/dist/gql/scalars/Bytes.js +32 -0
- package/dist/gql/scalars/Bytes.js.map +1 -0
- package/dist/gql/scalars/DateTime.d.ts +3 -0
- package/dist/gql/scalars/DateTime.d.ts.map +1 -0
- package/dist/gql/scalars/DateTime.js +44 -0
- package/dist/gql/scalars/DateTime.js.map +1 -0
- package/dist/gql/scalars/JSON.d.ts +3 -0
- package/dist/gql/scalars/JSON.d.ts.map +1 -0
- package/dist/gql/scalars/JSON.js +9 -0
- package/dist/gql/scalars/JSON.js.map +1 -0
- package/dist/gql/scalars/index.d.ts +7 -0
- package/dist/gql/scalars/index.d.ts.map +1 -0
- package/dist/gql/scalars/index.js +14 -0
- package/dist/gql/scalars/index.js.map +1 -0
- package/dist/gql/schema.d.ts.map +1 -1
- package/dist/gql/schema.js +3 -2
- package/dist/gql/schema.js.map +1 -1
- package/dist/orderBy.d.ts.map +1 -1
- package/dist/orderBy.js +4 -2
- package/dist/orderBy.js.map +1 -1
- package/dist/queryBuilder.d.ts +3 -1
- package/dist/queryBuilder.d.ts.map +1 -1
- package/dist/queryBuilder.js +121 -24
- package/dist/queryBuilder.js.map +1 -1
- package/dist/resolver.d.ts +2 -1
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +12 -12
- package/dist/resolver.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3 -2
- package/dist/server.js.map +1 -1
- package/dist/test/basic.test.js +3 -3
- package/dist/test/basic.test.js.map +1 -1
- package/dist/test/connection.test.js +1 -1
- package/dist/test/connection.test.js.map +1 -1
- package/dist/test/fts.test.js +2 -2
- package/dist/test/fts.test.js.map +1 -1
- package/dist/test/isNull.test.js +1 -1
- package/dist/test/isNull.test.js.map +1 -1
- package/dist/test/lists.test.js +3 -3
- package/dist/test/lists.test.js.map +1 -1
- package/dist/test/lookup.test.js +13 -7
- package/dist/test/lookup.test.js.map +1 -1
- package/dist/test/regressions.test.d.ts +2 -0
- package/dist/test/regressions.test.d.ts.map +1 -0
- package/dist/test/regressions.test.js +39 -0
- package/dist/test/regressions.test.js.map +1 -0
- package/dist/test/scalars.test.js +47 -10
- package/dist/test/scalars.test.js.map +1 -1
- package/dist/test/{util/setup.d.ts → setup.d.ts} +8 -1
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/{util/setup.js → setup.js} +19 -8
- package/dist/test/setup.js.map +1 -0
- package/dist/test/typed-json.test.js +16 -2
- package/dist/test/typed-json.test.js.map +1 -1
- package/dist/test/unions.test.js +1 -1
- package/dist/test/unions.test.js.map +1 -1
- package/dist/test/where.test.js +1 -1
- package/dist/test/where.test.js.map +1 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +5 -1
- package/dist/util.js.map +1 -1
- package/dist/where.d.ts +1 -1
- package/dist/where.d.ts.map +1 -1
- package/dist/where.js +5 -1
- package/dist/where.js.map +1 -1
- package/package.json +5 -5
- package/src/dialect.ts +2 -0
- package/src/gql/opencrud.ts +15 -6
- package/src/gql/scalars/BigInt.ts +34 -0
- package/src/gql/scalars/Bytes.ts +28 -0
- package/src/gql/scalars/DateTime.ts +45 -0
- package/src/gql/scalars/JSON.ts +7 -0
- package/src/gql/scalars/index.ts +12 -0
- package/src/gql/schema.ts +3 -2
- package/src/orderBy.ts +4 -2
- package/src/queryBuilder.ts +114 -20
- package/src/resolver.ts +13 -11
- package/src/server.ts +5 -2
- package/src/test/basic.test.ts +3 -3
- package/src/test/connection.test.ts +1 -1
- package/src/test/fts.test.ts +4 -2
- package/src/test/isNull.test.ts +1 -1
- package/src/test/lists.test.ts +3 -3
- package/src/test/lookup.test.ts +13 -7
- package/src/test/regressions.test.ts +39 -0
- package/src/test/scalars.test.ts +49 -10
- package/src/test/{util/setup.ts → setup.ts} +21 -7
- package/src/test/typed-json.test.ts +17 -2
- package/src/test/unions.test.ts +1 -1
- package/src/test/where.test.ts +1 -1
- package/src/util.ts +5 -0
- package/src/where.ts +9 -2
- package/dist/scalars.d.ts +0 -36
- package/dist/scalars.d.ts.map +0 -1
- package/dist/scalars.js +0 -229
- package/dist/scalars.js.map +0 -1
- package/dist/test/util/setup.d.ts.map +0 -1
- package/dist/test/util/setup.js.map +0 -1
- 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, ...
|
|
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
|
|
32
|
-
let
|
|
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
|
|
package/src/test/basic.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useDatabase, useServer} from "./
|
|
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
|
{
|
package/src/test/fts.test.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {useDatabase, useServer} from "./
|
|
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
|
-
|
|
8
|
+
|
|
9
|
+
isCockroach() || describe('full text search', function () {
|
|
8
10
|
useDatabase([
|
|
9
11
|
`create table foo (
|
|
10
12
|
id text primary key,
|
package/src/test/isNull.test.ts
CHANGED
package/src/test/lists.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useDatabase, useServer} from "./
|
|
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.
|
|
176
|
-
'2021-01-01T00:00:00.
|
|
175
|
+
'2020-01-01T00:00:00.000000Z',
|
|
176
|
+
'2021-01-01T00:00:00.000000Z'
|
|
177
177
|
]
|
|
178
178
|
}]
|
|
179
179
|
})
|
package/src/test/lookup.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useDatabase, useServer} from "./
|
|
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: [
|
|
85
|
+
issues(orderBy: [payment_amount_ASC]) {
|
|
86
86
|
id
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
`, {
|
|
90
|
-
issues:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
+
})
|
package/src/test/scalars.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {useDatabase, useServer} from "./
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
243
|
-
{id: '11', dateTime: '2021-09-24T00:00:00.
|
|
244
|
-
{id: '12', dateTime: '2021-09-24T01:00:00.
|
|
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 {
|
|
5
|
-
import {
|
|
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
|
|
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
|
|
35
|
-
await client.query(`CREATE SCHEMA
|
|
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 "./
|
|
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 {
|
package/src/test/unions.test.ts
CHANGED
package/src/test/where.test.ts
CHANGED
package/src/util.ts
CHANGED
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
|
package/dist/scalars.d.ts.map
DELETED
|
@@ -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"}
|