@subsquid/openreader 0.6.0 → 1.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 (63) hide show
  1. package/README.md +1 -1
  2. package/dist/gql/opencrud.d.ts.map +1 -1
  3. package/dist/gql/opencrud.js +13 -8
  4. package/dist/gql/opencrud.js.map +1 -1
  5. package/dist/gql/scalars/Bytes.js +6 -6
  6. package/dist/gql/scalars/Bytes.js.map +1 -1
  7. package/dist/gql/scalars/JSON.d.ts +3 -0
  8. package/dist/gql/scalars/JSON.d.ts.map +1 -0
  9. package/dist/gql/scalars/JSON.js +9 -0
  10. package/dist/gql/scalars/JSON.js.map +1 -0
  11. package/dist/gql/scalars/index.d.ts +1 -0
  12. package/dist/gql/scalars/index.d.ts.map +1 -1
  13. package/dist/gql/scalars/index.js +3 -1
  14. package/dist/gql/scalars/index.js.map +1 -1
  15. package/dist/gql/schema.js +3 -3
  16. package/dist/gql/schema.js.map +1 -1
  17. package/dist/orderBy.d.ts.map +1 -1
  18. package/dist/orderBy.js +4 -2
  19. package/dist/orderBy.js.map +1 -1
  20. package/dist/queryBuilder.d.ts.map +1 -1
  21. package/dist/queryBuilder.js +38 -23
  22. package/dist/queryBuilder.js.map +1 -1
  23. package/dist/requestedFields.js +2 -2
  24. package/dist/requestedFields.js.map +1 -1
  25. package/dist/resolver.js +8 -8
  26. package/dist/resolver.js.map +1 -1
  27. package/dist/test/regressions.test.d.ts +2 -0
  28. package/dist/test/regressions.test.d.ts.map +1 -0
  29. package/dist/test/regressions.test.js +39 -0
  30. package/dist/test/regressions.test.js.map +1 -0
  31. package/dist/test/scalars.test.js +35 -1
  32. package/dist/test/scalars.test.js.map +1 -1
  33. package/dist/test/setup.js +2 -2
  34. package/dist/test/setup.js.map +1 -1
  35. package/dist/test/typed-json.test.js +15 -1
  36. package/dist/test/typed-json.test.js.map +1 -1
  37. package/dist/tools.d.ts +1 -0
  38. package/dist/tools.d.ts.map +1 -1
  39. package/dist/tools.js +20 -1
  40. package/dist/tools.js.map +1 -1
  41. package/dist/util.js +5 -5
  42. package/dist/util.js.map +1 -1
  43. package/dist/where.d.ts +1 -1
  44. package/dist/where.d.ts.map +1 -1
  45. package/dist/where.js +3 -1
  46. package/dist/where.js.map +1 -1
  47. package/package.json +12 -9
  48. package/src/gql/opencrud.ts +7 -1
  49. package/src/gql/scalars/Bytes.ts +1 -1
  50. package/src/gql/scalars/JSON.ts +7 -0
  51. package/src/gql/scalars/index.ts +3 -1
  52. package/src/gql/schema.ts +1 -1
  53. package/src/orderBy.ts +4 -2
  54. package/src/queryBuilder.ts +17 -3
  55. package/src/requestedFields.ts +1 -1
  56. package/src/resolver.ts +1 -1
  57. package/src/test/regressions.test.ts +39 -0
  58. package/src/test/scalars.test.ts +37 -1
  59. package/src/test/setup.ts +1 -1
  60. package/src/test/typed-json.test.ts +16 -1
  61. package/src/tools.ts +15 -0
  62. package/src/util.ts +1 -1
  63. package/src/where.ts +6 -2
package/dist/where.js CHANGED
@@ -24,7 +24,9 @@ const ENDINGS = [
24
24
  'containsNone',
25
25
  'some',
26
26
  'every',
27
- 'none'
27
+ 'none',
28
+ 'jsonContains',
29
+ 'jsonHasKey',
28
30
  ].sort((a, b) => b.length - a.length).map(e => '_' + e);
29
31
  function parseEnding(field) {
30
32
  for (let i = 0; i < ENDINGS.length; i++) {
package/dist/where.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"where.js","sourceRoot":"","sources":["../src/where.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"where.js","sourceRoot":"","sources":["../src/where.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subsquid/openreader",
3
- "version": "0.6.0",
3
+ "version": "1.0.0",
4
4
  "description": "GraphQL server for squid framework",
5
5
  "keywords": [
6
6
  "graphql",
@@ -23,28 +23,31 @@
23
23
  "@graphql-tools/merge": "^8",
24
24
  "@graphql-tools/utils": "^8",
25
25
  "@subsquid/graphiql-console": "^0.2.0",
26
- "@subsquid/util": "^0.0.5",
26
+ "@subsquid/util-internal": "^0.0.0",
27
+ "@subsquid/util-internal-code-printer": "^0.0.0",
28
+ "@subsquid/util-naming": "^0.0.0",
29
+ "@subsquid/util-internal-hex": "^0.0.0",
27
30
  "apollo-server-core": "^3.6.2",
28
31
  "apollo-server-express": "^3.6.2",
29
- "express": "^4.17.2",
32
+ "express": "^4.18.1",
30
33
  "graphql": "^15.8.0",
31
34
  "graphql-parse-resolve-info": "^4.12.0",
32
35
  "pg": "^8.7.3"
33
36
  },
34
37
  "devDependencies": {
35
38
  "@types/express": "^4.17.13",
36
- "@types/mocha": "^9.1.0",
39
+ "@types/mocha": "^9.1.1",
37
40
  "@types/node": "^16.11.22",
38
- "@types/pg": "^8.6.4",
41
+ "@types/pg": "8.6.4",
39
42
  "dotenv": "^10.0.0",
40
- "expect": "^27.4.6",
43
+ "expect": "^27.5.1",
41
44
  "gql-test-client": "^0.0.0",
42
- "mocha": "^9.2.0",
45
+ "mocha": "^9.2.2",
43
46
  "typescript": "~4.5.5"
44
47
  },
45
48
  "scripts": {
46
49
  "build": "rm -rf dist && tsc",
47
- "test": "make up && sleep 1 && make test test-cockroach && make down || (make down && exit 1)"
50
+ "test": "make up && sleep 2 && make test test-cockroach && make down || (make down && exit 1)"
48
51
  },
49
- "readme": "# OpenReader\n\nGraphQL server for squid framework. Given [data schema](https://docs.subsquid.io/schema-spec) \nand compatible Postgres database it serves \"read part\" of [OpenCRUD spec](https://www.opencrud.org).\n\n## Usage\n\n```bash\nopenreader schema.graphql\n```\n\nDatabase connection and server port are configured via environment variables:\n\n```\nDB_NAME\nDB_USER\nDB_PASS\nDB_HOST\nDB_PORT\nGRAPHQL_SERVER_PORT\n```\n"
52
+ "readme": "# OpenReader\n\nGraphQL server for squid framework. Given [data schema](https://docs.subsquid.io/reference/openreader-schema) \nand compatible Postgres database it serves \"read part\" of [OpenCRUD spec](https://www.opencrud.org).\n\n## Usage\n\n```bash\nopenreader schema.graphql\n```\n\nDatabase connection and server port are configured via environment variables:\n\n```\nDB_NAME\nDB_USER\nDB_PASS\nDB_HOST\nDB_PORT\nGRAPHQL_SERVER_PORT\n```\n"
50
53
  }
@@ -1,4 +1,5 @@
1
- import {Output, toCamelCase, toPlural} from "@subsquid/util"
1
+ import {Output} from "@subsquid/util-internal-code-printer"
2
+ import {toCamelCase, toPlural} from "@subsquid/util-naming"
2
3
  import assert from "assert"
3
4
  import {DocumentNode, parse, print} from "graphql"
4
5
  import type {Dialect} from "../dialect"
@@ -264,6 +265,11 @@ export function generateOpenCrudQueries(model: Model, dialect: Dialect): string
264
265
  out.line(`${fieldName}_in: [${graphqlType}!]`)
265
266
  out.line(`${fieldName}_not_in: [${graphqlType}!]`)
266
267
  }
268
+
269
+ if (graphqlType == 'JSON') {
270
+ out.line(`${fieldName}_jsonContains: ${graphqlType}`)
271
+ out.line(`${fieldName}_jsonHasKey: ${graphqlType}`)
272
+ }
267
273
  }
268
274
 
269
275
  function generateUnionType(name: string, union: Union) {
@@ -1,4 +1,4 @@
1
- import {decodeHex, isHex} from "@subsquid/util"
1
+ import {decodeHex, isHex} from "@subsquid/util-internal-hex"
2
2
  import {GraphQLScalarType} from "graphql"
3
3
  import {invalidFormat} from "../../util"
4
4
 
@@ -0,0 +1,7 @@
1
+ import {GraphQLScalarType} from "graphql"
2
+
3
+
4
+ export const JSONScalar = new GraphQLScalarType({
5
+ name: 'JSON',
6
+ description: 'A scalar that can represent any JSON value',
7
+ })
@@ -1,10 +1,12 @@
1
1
  import {BigIntScalar} from "./BigInt"
2
2
  import {BytesScalar} from "./Bytes"
3
3
  import {DateTimeScalar} from "./DateTime"
4
+ import {JSONScalar} from "./JSON"
4
5
 
5
6
 
6
7
  export const customScalars = {
7
8
  BigInt: BigIntScalar,
8
9
  Bytes: BytesScalar,
9
- DateTime: DateTimeScalar
10
+ DateTime: DateTimeScalar,
11
+ JSON: JSONScalar,
10
12
  }
package/src/gql/schema.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {assertNotNull, unexpectedCase} from "@subsquid/util"
1
+ import {assertNotNull, unexpectedCase} from "@subsquid/util-internal"
2
2
  import assert from "assert"
3
3
  import {
4
4
  buildASTSchema,
package/src/orderBy.ts CHANGED
@@ -50,8 +50,10 @@ function buildOrderByMapping(model: Model, typeName: string, depth: number): Ope
50
50
  switch(propType.kind) {
51
51
  case 'scalar':
52
52
  case 'enum':
53
- m.set(key + '_ASC', {[key]: 'ASC'})
54
- m.set(key + '_DESC', {[key]: 'DESC'})
53
+ if (propType.name != 'JSON') {
54
+ m.set(key + '_ASC', {[key]: 'ASC'})
55
+ m.set(key + '_DESC', {[key]: 'DESC'})
56
+ }
55
57
  break
56
58
  case 'object':
57
59
  case 'union':
@@ -1,4 +1,4 @@
1
- import {toSnakeCase} from "@subsquid/util"
1
+ import {toSnakeCase} from "@subsquid/util-naming"
2
2
  import assert from "assert"
3
3
  import {Database} from "./db"
4
4
  import type {Dialect} from "./dialect"
@@ -369,6 +369,12 @@ export class QueryBuilder {
369
369
  case 'not_containsInsensitive':
370
370
  exps.push(`position(lower(${this.param(arg)}) in lower(${lhs})) = 0`)
371
371
  break
372
+ case 'jsonContains':
373
+ exps.push(`${lhs} @> ${this.param(arg)}`)
374
+ break
375
+ case 'jsonHasKey':
376
+ exps.push(`${lhs} ? ${this.param(arg)}`)
377
+ break
372
378
  default: {
373
379
  exps.push(`${lhs} ${whereOpToSqlOperator(op)} ${this.param(arg)}`)
374
380
  }
@@ -465,9 +471,15 @@ export class QueryBuilder {
465
471
  }
466
472
  break
467
473
  }
468
- case 'list-lookup':
469
- rec[req.alias] = this.toResult(row[req.index], req.children)
474
+ case 'list-lookup': {
475
+ let rows = row[req.index]
476
+ if (rows == null) {
477
+ rec[req.alias] = []
478
+ } else {
479
+ rec[req.alias] = this.toResult(row[req.index], req.children)
480
+ }
470
481
  break
482
+ }
471
483
  default:
472
484
  throw unsupportedCase((f as any).propType.kind)
473
485
  }
@@ -624,6 +636,8 @@ class Cursor {
624
636
  return `(${this.prefix}->'${propName}')::numeric`
625
637
  case 'Boolean':
626
638
  return `(${this.prefix}->>'${propName}')::bool`
639
+ case 'JSON':
640
+ return `${this.prefix}->'${propName}'`
627
641
  default:
628
642
  return `${this.prefix}->>'${propName}'`
629
643
  }
@@ -1,4 +1,4 @@
1
- import {toPlural} from "@subsquid/util"
1
+ import {toPlural} from "@subsquid/util-naming"
2
2
  import {UserInputError} from "apollo-server-core"
3
3
  import assert from "assert"
4
4
  import {GraphQLResolveInfo, GraphQLSchema} from "graphql"
package/src/resolver.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type {IFieldResolver, IResolvers} from "@graphql-tools/utils"
2
- import {toCamelCase} from "@subsquid/util"
2
+ import {toCamelCase} from "@subsquid/util-naming"
3
3
  import {UserInputError} from "apollo-server-core"
4
4
  import assert from "assert"
5
5
  import type {GraphQLResolveInfo} from "graphql"
@@ -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
+ })
@@ -2,7 +2,7 @@ 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)`,
@@ -21,6 +21,8 @@ describe('scalars', function() {
21
21
  `insert into scalar (id, "enum") values ('15', 'A')`,
22
22
  `insert into scalar (id, "enum") values ('16', 'B')`,
23
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)`,
24
26
  ])
25
27
 
26
28
  const client = useServer(`
@@ -32,6 +34,7 @@ describe('scalars', function() {
32
34
  bigint: BigInt
33
35
  dateTime: DateTime
34
36
  bytes: Bytes
37
+ json: JSON,
35
38
  deep: DeepScalar
36
39
  }
37
40
 
@@ -316,4 +319,37 @@ describe('scalars', function() {
316
319
  })
317
320
  })
318
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
+ })
319
355
  })
package/src/test/setup.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {assertNotNull} from "@subsquid/util"
1
+ import {assertNotNull} from "@subsquid/util-internal"
2
2
  import {Client} from "gql-test-client"
3
3
  import {parse} from "graphql"
4
4
  import {Client as PgClient, ClientBase, Pool} from "pg"
@@ -3,7 +3,7 @@ import {useDatabase, useServer} from "./setup"
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/tools.ts CHANGED
@@ -2,6 +2,7 @@ import {mergeTypeDefs} from "@graphql-tools/merge"
2
2
  import * as fs from "fs"
3
3
  import * as path from "path"
4
4
  import {parse, Source} from "graphql"
5
+ import process from "process"
5
6
  import {buildModel, buildSchema} from "./gql/schema"
6
7
  import type {Model} from "./model"
7
8
 
@@ -31,3 +32,17 @@ export function loadModel(schemaFile: string): Model {
31
32
  let schema = buildSchema(doc)
32
33
  return buildModel(schema)
33
34
  }
35
+
36
+
37
+ export function resolveGraphqlSchema(projectDir?: string): string {
38
+ let dir = projectDir || process.cwd()
39
+ let loc = path.resolve(dir, 'schema.graphql')
40
+ if (fs.existsSync(loc)) return loc
41
+ loc = path.resolve(dir, 'schema')
42
+ let stat = fs.statSync(loc, {throwIfNoEntry: false})
43
+ if (stat?.isDirectory()) {
44
+ let hasGraphql = fs.readdirSync(loc).some(item => item.endsWith('.graphql') || item.endsWith('.gql'))
45
+ if (hasGraphql) return loc
46
+ }
47
+ throw new Error(`Failed to locate schema.graphql at ${dir}`)
48
+ }
package/src/util.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {toCamelCase, toPlural, toSnakeCase} from "@subsquid/util"
1
+ import {toCamelCase, toPlural, toSnakeCase} from "@subsquid/util-naming"
2
2
  import assert from "assert"
3
3
 
4
4
 
package/src/where.ts CHANGED
@@ -17,7 +17,9 @@ export type WhereOp =
17
17
  'containsNone' |
18
18
  'some' |
19
19
  'every' |
20
- 'none'
20
+ 'none' |
21
+ 'jsonContains' |
22
+ 'jsonHasKey'
21
23
 
22
24
 
23
25
  const ENDINGS = [
@@ -43,7 +45,9 @@ const ENDINGS = [
43
45
  'containsNone',
44
46
  'some',
45
47
  'every',
46
- 'none'
48
+ 'none',
49
+ 'jsonContains',
50
+ 'jsonHasKey',
47
51
  ].sort((a, b) => b.length - a.length).map(e => '_' + e)
48
52
 
49
53