@platformatic/sql-graphql 0.1.0 → 0.3.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.
- package/.nyc_output/28567d89-45dc-4aa4-8d9d-4c8b713612e5.json +1 -0
- package/.nyc_output/{22ead0f6-e5a6-4647-a2ea-db2f42498081.json → 475e591b-b773-4ada-87d0-db538511c660.json} +0 -0
- package/.nyc_output/4956602f-f6bd-4aef-bc4c-3d019bf6d754.json +1 -0
- package/.nyc_output/4c93feaa-384f-4a7c-b7bf-070cecebf72c.json +1 -0
- package/.nyc_output/6e295e9b-655e-43fd-a92a-75d058bed16d.json +1 -0
- package/.nyc_output/79fec06e-8965-498f-a023-624db14cdca7.json +1 -0
- package/.nyc_output/7f365fa0-3cdb-4303-ad17-59e3639745cc.json +1 -0
- package/.nyc_output/92f8aaa6-0c3d-4833-8544-93e0be94a868.json +1 -0
- package/.nyc_output/a4edd7d2-26a6-46ca-a79e-bc52a9308d76.json +1 -0
- package/.nyc_output/b152f368-1d63-4d9d-bd3a-97bb6b761ca8.json +1 -0
- package/.nyc_output/cc4af553-54c0-4a1e-9980-b7d8534e9010.json +1 -0
- package/.nyc_output/e0eb89e2-5c3c-4b39-8237-5827918c9774.json +1 -0
- package/.nyc_output/e640b1f5-34e9-4ac5-8691-3fc4b925739b.json +1 -0
- package/.nyc_output/f3e7d9f6-b598-48c5-9ebe-18071958a7d8.json +1 -0
- package/.nyc_output/fb0cb3f2-69da-49fa-bd3c-f13eceee0d3d.json +1 -0
- package/.nyc_output/processinfo/{191054d4-6a1e-4a1b-87bb-f801ed78a223.json → 28567d89-45dc-4aa4-8d9d-4c8b713612e5.json} +1 -1
- package/.nyc_output/processinfo/475e591b-b773-4ada-87d0-db538511c660.json +1 -0
- package/.nyc_output/processinfo/{5acfd416-02d1-497b-8dbd-69c0af167c62.json → 4956602f-f6bd-4aef-bc4c-3d019bf6d754.json} +1 -1
- package/.nyc_output/processinfo/{74cc1dab-7479-48e7-80b0-c97351d8856e.json → 4c93feaa-384f-4a7c-b7bf-070cecebf72c.json} +1 -1
- package/.nyc_output/processinfo/{c201a1cd-3105-4d18-bbbb-8d7fa674d439.json → 6e295e9b-655e-43fd-a92a-75d058bed16d.json} +1 -1
- package/.nyc_output/processinfo/{0681b2b7-587a-40f2-8242-257bc4c72456.json → 79fec06e-8965-498f-a023-624db14cdca7.json} +1 -1
- package/.nyc_output/processinfo/{5ec04f76-9bcb-475d-b79e-f9304cc0026c.json → 7f365fa0-3cdb-4303-ad17-59e3639745cc.json} +1 -1
- package/.nyc_output/processinfo/{4e847e26-05cd-4063-9277-48aff86d6822.json → 92f8aaa6-0c3d-4833-8544-93e0be94a868.json} +1 -1
- package/.nyc_output/processinfo/{c1c0eecb-63a0-4258-81b6-50e7488bd67c.json → a4edd7d2-26a6-46ca-a79e-bc52a9308d76.json} +1 -1
- package/.nyc_output/processinfo/b152f368-1d63-4d9d-bd3a-97bb6b761ca8.json +1 -0
- package/.nyc_output/processinfo/{d6da0270-e9ed-41e2-bbf3-9262d1df683c.json → cc4af553-54c0-4a1e-9980-b7d8534e9010.json} +1 -1
- package/.nyc_output/processinfo/{5f3bb47a-0d2d-440a-a466-8ffa9ab3efaa.json → e0eb89e2-5c3c-4b39-8237-5827918c9774.json} +1 -1
- package/.nyc_output/processinfo/{9f5d12ba-bc3d-461c-a0e2-2a95d2020c19.json → e640b1f5-34e9-4ac5-8691-3fc4b925739b.json} +1 -1
- package/.nyc_output/processinfo/{ac343b71-6f76-421c-a287-46d19bb48f89.json → f3e7d9f6-b598-48c5-9ebe-18071958a7d8.json} +1 -1
- package/.nyc_output/processinfo/{71c2bd84-9a72-4e86-83c6-d587ce525fe4.json → fb0cb3f2-69da-49fa-bd3c-f13eceee0d3d.json} +1 -1
- package/.nyc_output/processinfo/index.json +1 -1
- package/index.d.ts +0 -1
- package/index.js +37 -33
- package/lib/entity-to-type.js +32 -8
- package/lib/relationship.js +1 -1
- package/lib/utils.js +3 -0
- package/package.json +4 -2
- package/test/count.test.js +126 -0
- package/test/helper.js +1 -0
- package/test/simple.test.js +196 -1
- package/test/types/index.test-d.ts +0 -1
- package/.nyc_output/0681b2b7-587a-40f2-8242-257bc4c72456.json +0 -1
- package/.nyc_output/191054d4-6a1e-4a1b-87bb-f801ed78a223.json +0 -1
- package/.nyc_output/4e847e26-05cd-4063-9277-48aff86d6822.json +0 -1
- package/.nyc_output/5acfd416-02d1-497b-8dbd-69c0af167c62.json +0 -1
- package/.nyc_output/5ec04f76-9bcb-475d-b79e-f9304cc0026c.json +0 -1
- package/.nyc_output/5f3bb47a-0d2d-440a-a466-8ffa9ab3efaa.json +0 -1
- package/.nyc_output/71c2bd84-9a72-4e86-83c6-d587ce525fe4.json +0 -1
- package/.nyc_output/74cc1dab-7479-48e7-80b0-c97351d8856e.json +0 -1
- package/.nyc_output/9f5d12ba-bc3d-461c-a0e2-2a95d2020c19.json +0 -1
- package/.nyc_output/ac343b71-6f76-421c-a287-46d19bb48f89.json +0 -1
- package/.nyc_output/c1c0eecb-63a0-4258-81b6-50e7488bd67c.json +0 -1
- package/.nyc_output/c201a1cd-3105-4d18-bbbb-8d7fa674d439.json +0 -1
- package/.nyc_output/d6da0270-e9ed-41e2-bbf3-9262d1df683c.json +0 -1
- package/.nyc_output/processinfo/22ead0f6-e5a6-4647-a2ea-db2f42498081.json +0 -1
package/index.js
CHANGED
|
@@ -9,7 +9,6 @@ const scalars = require('graphql-scalars')
|
|
|
9
9
|
|
|
10
10
|
async function mapperToGraphql (app, opts) {
|
|
11
11
|
const mapper = app.platformatic
|
|
12
|
-
const autoTimestamp = opts.autoTimestamp
|
|
13
12
|
const queryTopFields = {}
|
|
14
13
|
const mutationTopFields = {}
|
|
15
14
|
const resolvers = {}
|
|
@@ -23,42 +22,48 @@ async function mapperToGraphql (app, opts) {
|
|
|
23
22
|
resolvers,
|
|
24
23
|
loaders,
|
|
25
24
|
federationReplacements,
|
|
26
|
-
federationMetadata: opts.federationMetadata
|
|
27
|
-
autoTimestamp
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const metaMap = new Map()
|
|
31
|
-
for (const entity of Object.values(mapper.entities)) {
|
|
32
|
-
relations.push(...entity.relations)
|
|
33
|
-
const meta = constructGraph(app, entity, graphOpts)
|
|
34
|
-
metaMap.set(entity, meta)
|
|
25
|
+
federationMetadata: opts.federationMetadata
|
|
35
26
|
}
|
|
27
|
+
if (Object.keys(mapper.entities).length === 0) {
|
|
28
|
+
// no schema
|
|
29
|
+
queryTopFields.hello = { type: graphql.GraphQLString }
|
|
30
|
+
resolvers.Query = {
|
|
31
|
+
hello: () => 'Hello Platformatic!'
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
const metaMap = new Map()
|
|
35
|
+
for (const entity of Object.values(mapper.entities)) {
|
|
36
|
+
relations.push(...entity.relations)
|
|
37
|
+
const meta = constructGraph(app, entity, graphOpts)
|
|
38
|
+
metaMap.set(entity, meta)
|
|
39
|
+
}
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
establishRelations(app, relations, resolvers, loaders, queryTopFields, opts.resolvers || {}, metaMap)
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
if (opts.resolvers) {
|
|
44
|
+
for (const key of Object.keys(opts.resolvers)) {
|
|
45
|
+
if (!resolvers[key]) {
|
|
46
|
+
resolvers[key] = {}
|
|
47
|
+
}
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
const type = opts.resolvers[key]
|
|
50
|
+
for (const resolver of Object.keys(type)) {
|
|
51
|
+
if (type[resolver] === false) {
|
|
52
|
+
if (resolvers[key][resolver]) {
|
|
53
|
+
delete resolvers[key][resolver]
|
|
54
|
+
}
|
|
55
|
+
if (loaders[key]) {
|
|
56
|
+
delete loaders[key][resolver]
|
|
57
|
+
}
|
|
58
|
+
/* istanbul ignore else */
|
|
59
|
+
if (key === 'Mutation') {
|
|
60
|
+
delete mutationTopFields[resolver]
|
|
61
|
+
} else if (key === 'Query') {
|
|
62
|
+
delete queryTopFields[resolver]
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
resolvers[key][resolver] = type[resolver]
|
|
53
66
|
}
|
|
54
|
-
/* istanbul ignore else */
|
|
55
|
-
if (key === 'Mutation') {
|
|
56
|
-
delete mutationTopFields[resolver]
|
|
57
|
-
} else if (key === 'Query') {
|
|
58
|
-
delete queryTopFields[resolver]
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
resolvers[key][resolver] = type[resolver]
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
}
|
|
@@ -93,7 +98,6 @@ async function mapperToGraphql (app, opts) {
|
|
|
93
98
|
sdl += '\n'
|
|
94
99
|
sdl += opts.schema
|
|
95
100
|
}
|
|
96
|
-
|
|
97
101
|
// Ignoriring because SQLite doesn't support dates
|
|
98
102
|
/* istanbul ignore next */
|
|
99
103
|
if (sdl.match(/scalar Date\n/)) {
|
package/lib/entity-to-type.js
CHANGED
|
@@ -16,12 +16,12 @@ const ascDesc = new graphql.GraphQLEnumType({
|
|
|
16
16
|
})
|
|
17
17
|
|
|
18
18
|
function constructGraph (app, entity, opts) {
|
|
19
|
-
const primaryKey = entity.primaryKey
|
|
20
|
-
|
|
19
|
+
const primaryKey = camelcase(entity.primaryKey)
|
|
20
|
+
const relationalFields = entity.relations
|
|
21
|
+
.map((relation) => relation.column_name)
|
|
21
22
|
const entityName = entity.name
|
|
22
23
|
const singular = entity.singularName
|
|
23
24
|
const plural = entity.pluralName
|
|
24
|
-
|
|
25
25
|
const {
|
|
26
26
|
queryTopFields,
|
|
27
27
|
mutationTopFields,
|
|
@@ -128,7 +128,7 @@ function constructGraph (app, entity, opts) {
|
|
|
128
128
|
resolvers.Query[plural] = (_, query, ctx, info) => {
|
|
129
129
|
const requestedFields = info.fieldNodes[0].selectionSet.selections.map((s) => s.name.value)
|
|
130
130
|
requestedFields.push(primaryKey)
|
|
131
|
-
return entity.find({ ...query, fields: requestedFields, ctx })
|
|
131
|
+
return entity.find({ ...query, fields: [...requestedFields, ...relationalFields], ctx })
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
const inputType = new graphql.GraphQLInputObjectType({
|
|
@@ -143,6 +143,29 @@ function constructGraph (app, entity, opts) {
|
|
|
143
143
|
}, {})
|
|
144
144
|
})
|
|
145
145
|
|
|
146
|
+
const count = camelcase(['count', plural])
|
|
147
|
+
|
|
148
|
+
const countType = new graphql.GraphQLObjectType({
|
|
149
|
+
name: `${plural}Count`,
|
|
150
|
+
fields: {
|
|
151
|
+
total: { type: graphql.GraphQLInt }
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
queryTopFields[count] = {
|
|
156
|
+
type: countType,
|
|
157
|
+
args: {
|
|
158
|
+
where: { type: whereArgType }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
resolvers.Query[count] = async (_, query, ctx, info) => {
|
|
163
|
+
const requestedFields = info.fieldNodes[0].selectionSet.selections.map((s) => s.name.value)
|
|
164
|
+
requestedFields.push(primaryKey)
|
|
165
|
+
const total = await entity.count({ ...query, fields: [...requestedFields, ...relationalFields], ctx })
|
|
166
|
+
return { total }
|
|
167
|
+
}
|
|
168
|
+
|
|
146
169
|
const save = camelcase(['save', singular])
|
|
147
170
|
|
|
148
171
|
mutationTopFields[save] = {
|
|
@@ -154,7 +177,7 @@ function constructGraph (app, entity, opts) {
|
|
|
154
177
|
|
|
155
178
|
resolvers.Mutation[save] = async (_, { input }, ctx, info) => {
|
|
156
179
|
const fields = fromSelectionSet(info.fieldNodes[0].selectionSet)
|
|
157
|
-
return entity.save({ input, ctx, fields: [...fields] })
|
|
180
|
+
return entity.save({ input, ctx, fields: [...fields, ...relationalFields] })
|
|
158
181
|
}
|
|
159
182
|
|
|
160
183
|
const insert = camelcase(['insert', plural])
|
|
@@ -168,7 +191,7 @@ function constructGraph (app, entity, opts) {
|
|
|
168
191
|
|
|
169
192
|
resolvers.Mutation[insert] = (_, { inputs }, ctx, info) => {
|
|
170
193
|
const fields = fromSelectionSet(info.fieldNodes[0].selectionSet)
|
|
171
|
-
return entity.insert({ inputs, ctx, fields: [...fields] })
|
|
194
|
+
return entity.insert({ inputs, ctx, fields: [...fields, ...relationalFields] })
|
|
172
195
|
}
|
|
173
196
|
|
|
174
197
|
const deleteKey = camelcase(['delete', plural])
|
|
@@ -181,7 +204,7 @@ function constructGraph (app, entity, opts) {
|
|
|
181
204
|
|
|
182
205
|
resolvers.Mutation[deleteKey] = (_, args, ctx, info) => {
|
|
183
206
|
const fields = info.fieldNodes[0].selectionSet.selections.map((s) => s.name.value)
|
|
184
|
-
return entity.delete({ ...args, fields, ctx })
|
|
207
|
+
return entity.delete({ ...args, fields: [...fields, ...relationalFields], ctx })
|
|
185
208
|
}
|
|
186
209
|
|
|
187
210
|
federationReplacements.push({
|
|
@@ -236,11 +259,12 @@ function constructGraph (app, entity, opts) {
|
|
|
236
259
|
}
|
|
237
260
|
|
|
238
261
|
function getFields (queries) {
|
|
239
|
-
const fields = new Set()
|
|
262
|
+
const fields = new Set([...relationalFields])
|
|
240
263
|
fields.add(primaryKey)
|
|
241
264
|
for (const query of queries) {
|
|
242
265
|
fromSelectionSet(query.info.fieldNodes[0].selectionSet, fields)
|
|
243
266
|
}
|
|
267
|
+
fields.delete(undefined)
|
|
244
268
|
return [...fields]
|
|
245
269
|
}
|
|
246
270
|
}
|
package/lib/relationship.js
CHANGED
|
@@ -51,7 +51,7 @@ module.exports = function establishRelations (app, relations, resolvers, loaders
|
|
|
51
51
|
resolvers[foreign.type] = resolvers[foreign.type] || {}
|
|
52
52
|
resolvers[foreign.type][lowered] = async function (obj, args, ctx, info) {
|
|
53
53
|
const fields = fromSelectionSet(info.fieldNodes[0].selectionSet, new Set())
|
|
54
|
-
const toSearch = { ...args, fields: [...fields], ctx }
|
|
54
|
+
const toSearch = { ...args, fields: [...fields, relation.column_name], ctx }
|
|
55
55
|
toSearch.where = toSearch.where || {}
|
|
56
56
|
toSearch.where[camelcase(relation.column_name)] = { eq: obj.id }
|
|
57
57
|
return current.entity.find(toSearch)
|
package/lib/utils.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const graphql = require('graphql')
|
|
4
4
|
const scalars = require('graphql-scalars')
|
|
5
|
+
const { GraphQLJSONObject } = require('graphql-type-json')
|
|
5
6
|
|
|
6
7
|
// The sqlTypeToGraphQL is shared between
|
|
7
8
|
// all database adapters.
|
|
@@ -51,6 +52,8 @@ function sqlTypeToGraphQL (sqlType) {
|
|
|
51
52
|
return scalars.GraphQLDateTime
|
|
52
53
|
case 'uuid':
|
|
53
54
|
return graphql.GraphQLString
|
|
55
|
+
case 'json':
|
|
56
|
+
return GraphQLJSONObject
|
|
54
57
|
default:
|
|
55
58
|
return graphql.GraphQLString
|
|
56
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/sql-graphql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Map SQL dbs to GraphQL",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"homepage": "https://github.com/plaformatic/platformatic#readme",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@platformatic/sql-mapper": "0.
|
|
17
|
+
"@platformatic/sql-mapper": "0.3.0",
|
|
18
18
|
"fastify": "^4.6.0",
|
|
19
19
|
"snazzy": "^9.0.0",
|
|
20
20
|
"standard": "^17.0.0",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"fastify-plugin": "^4.1.0",
|
|
27
27
|
"graphql": "^16.6.0",
|
|
28
28
|
"graphql-scalars": "^1.13.1",
|
|
29
|
+
"graphql-type-json": "^0.3.2",
|
|
29
30
|
"inflected": "^2.1.0",
|
|
30
31
|
"mercurius": "^11.0.0"
|
|
31
32
|
},
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"directory": "test/types"
|
|
34
35
|
},
|
|
35
36
|
"scripts": {
|
|
37
|
+
"lint": "standard",
|
|
36
38
|
"test": "standard | snazzy && npm run test:typescript && npm run test:postgresql && npm run test:mariadb && npm run test:mysql && npm run test:mysql8 && npm run test:sqlite",
|
|
37
39
|
"test:postgresql": "DB=postgresql tap test/*.test.js",
|
|
38
40
|
"test:mariadb": "DB=mariadb tap test/*.test.js",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const sqlGraphQL = require('..')
|
|
5
|
+
const sqlMapper = require('@platformatic/sql-mapper')
|
|
6
|
+
const fastify = require('fastify')
|
|
7
|
+
const { clear, connInfo, isSQLite } = require('./helper')
|
|
8
|
+
|
|
9
|
+
test('count', async ({ pass, teardown, same, equal }) => {
|
|
10
|
+
const app = fastify()
|
|
11
|
+
app.register(sqlMapper, {
|
|
12
|
+
...connInfo,
|
|
13
|
+
async onDatabaseLoad (db, sql) {
|
|
14
|
+
pass('onDatabaseLoad called')
|
|
15
|
+
|
|
16
|
+
await clear(db, sql)
|
|
17
|
+
|
|
18
|
+
if (isSQLite) {
|
|
19
|
+
await db.query(sql`CREATE TABLE posts (
|
|
20
|
+
id INTEGER PRIMARY KEY,
|
|
21
|
+
title VARCHAR(42),
|
|
22
|
+
long_text TEXT,
|
|
23
|
+
counter INTEGER
|
|
24
|
+
);`)
|
|
25
|
+
} else {
|
|
26
|
+
await db.query(sql`CREATE TABLE posts (
|
|
27
|
+
id SERIAL PRIMARY KEY,
|
|
28
|
+
title VARCHAR(42),
|
|
29
|
+
long_text TEXT,
|
|
30
|
+
counter INTEGER
|
|
31
|
+
);`)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
app.register(sqlGraphQL)
|
|
36
|
+
teardown(app.close.bind(app))
|
|
37
|
+
|
|
38
|
+
await app.ready()
|
|
39
|
+
|
|
40
|
+
const posts = [{
|
|
41
|
+
title: 'Dog',
|
|
42
|
+
longText: 'Foo',
|
|
43
|
+
counter: 10
|
|
44
|
+
}, {
|
|
45
|
+
title: 'Cat',
|
|
46
|
+
longText: 'Bar',
|
|
47
|
+
counter: 20
|
|
48
|
+
}, {
|
|
49
|
+
title: 'Mouse',
|
|
50
|
+
longText: 'Baz',
|
|
51
|
+
counter: 30
|
|
52
|
+
}, {
|
|
53
|
+
title: 'Duck',
|
|
54
|
+
longText: 'A duck tale',
|
|
55
|
+
counter: 40
|
|
56
|
+
}]
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
const res = await app.inject({
|
|
60
|
+
method: 'POST',
|
|
61
|
+
url: '/graphql',
|
|
62
|
+
body: {
|
|
63
|
+
query: `
|
|
64
|
+
mutation batch($inputs : [PostInput]!) {
|
|
65
|
+
insertPosts(inputs: $inputs) {
|
|
66
|
+
id
|
|
67
|
+
title
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`,
|
|
71
|
+
variables: {
|
|
72
|
+
inputs: posts
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
equal(res.statusCode, 200, 'posts status code')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
const res = await app.inject({
|
|
81
|
+
method: 'POST',
|
|
82
|
+
url: '/graphql',
|
|
83
|
+
body: {
|
|
84
|
+
query: `
|
|
85
|
+
query {
|
|
86
|
+
countPosts {
|
|
87
|
+
total
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
equal(res.statusCode, 200, 'posts status code')
|
|
94
|
+
same(res.json(), {
|
|
95
|
+
data: {
|
|
96
|
+
countPosts: {
|
|
97
|
+
total: 4
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}, 'posts response')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
{
|
|
104
|
+
const res = await app.inject({
|
|
105
|
+
method: 'POST',
|
|
106
|
+
url: '/graphql',
|
|
107
|
+
body: {
|
|
108
|
+
query: `
|
|
109
|
+
query {
|
|
110
|
+
countPosts(where: { counter: { gt: 20 } }) {
|
|
111
|
+
total
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
`
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
equal(res.statusCode, 200, 'posts status code')
|
|
118
|
+
same(res.json(), {
|
|
119
|
+
data: {
|
|
120
|
+
countPosts: {
|
|
121
|
+
total: 2
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}, 'posts response')
|
|
125
|
+
}
|
|
126
|
+
})
|
package/test/helper.js
CHANGED
|
@@ -13,6 +13,7 @@ if (!process.env.DB || process.env.DB === 'postgresql') {
|
|
|
13
13
|
connInfo.connectionString = 'mysql://root@127.0.0.1:3307/graph'
|
|
14
14
|
connInfo.poolSize = 10
|
|
15
15
|
module.exports.isMysql = true
|
|
16
|
+
module.exports.isMariaDB = true
|
|
16
17
|
} else if (process.env.DB === 'mysql') {
|
|
17
18
|
connInfo.connectionString = 'mysql://root@127.0.0.1/graph'
|
|
18
19
|
connInfo.poolSize = 10
|
package/test/simple.test.js
CHANGED
|
@@ -4,7 +4,7 @@ const { test } = require('tap')
|
|
|
4
4
|
const sqlGraphQL = require('..')
|
|
5
5
|
const sqlMapper = require('@platformatic/sql-mapper')
|
|
6
6
|
const fastify = require('fastify')
|
|
7
|
-
const { clear, connInfo, isSQLite, isMysql } = require('./helper')
|
|
7
|
+
const { clear, connInfo, isSQLite, isMysql, isMariaDB } = require('./helper')
|
|
8
8
|
|
|
9
9
|
async function createBasicPages (db, sql) {
|
|
10
10
|
if (isSQLite) {
|
|
@@ -706,3 +706,198 @@ test('graphiql can be disabled', async ({ pass, teardown, same, equal }) => {
|
|
|
706
706
|
const res = await app.inject('/graphiql')
|
|
707
707
|
equal(res.statusCode, 404)
|
|
708
708
|
})
|
|
709
|
+
|
|
710
|
+
test('default query hello should be created when no entities are found', async ({ pass, teardown, same, equal }) => {
|
|
711
|
+
const app = fastify()
|
|
712
|
+
app.register(sqlMapper, {
|
|
713
|
+
...connInfo,
|
|
714
|
+
async onDatabaseLoad (db, sql) {
|
|
715
|
+
pass('onDatabaseLoad called')
|
|
716
|
+
await clear(db, sql)
|
|
717
|
+
}
|
|
718
|
+
})
|
|
719
|
+
app.register(sqlGraphQL)
|
|
720
|
+
teardown(app.close.bind(app))
|
|
721
|
+
|
|
722
|
+
const res = await app.inject({
|
|
723
|
+
method: 'POST',
|
|
724
|
+
url: '/graphql',
|
|
725
|
+
body: {
|
|
726
|
+
query: 'query { hello }'
|
|
727
|
+
}
|
|
728
|
+
})
|
|
729
|
+
equal(res.statusCode, 200)
|
|
730
|
+
same(res.json(), {
|
|
731
|
+
data: {
|
|
732
|
+
hello: 'Hello Platformatic!'
|
|
733
|
+
}
|
|
734
|
+
})
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
test('default query hello should not be created when entities are found', async ({ pass, teardown, same, equal }) => {
|
|
738
|
+
const app = fastify()
|
|
739
|
+
app.register(sqlMapper, {
|
|
740
|
+
...connInfo,
|
|
741
|
+
async onDatabaseLoad (db, sql) {
|
|
742
|
+
pass('onDatabaseLoad called')
|
|
743
|
+
await clear(db, sql)
|
|
744
|
+
await createBasicPages(db, sql)
|
|
745
|
+
}
|
|
746
|
+
})
|
|
747
|
+
app.register(sqlGraphQL)
|
|
748
|
+
teardown(app.close.bind(app))
|
|
749
|
+
|
|
750
|
+
const res = await app.inject({
|
|
751
|
+
method: 'POST',
|
|
752
|
+
url: '/graphql',
|
|
753
|
+
body: {
|
|
754
|
+
query: 'query { hello }'
|
|
755
|
+
}
|
|
756
|
+
})
|
|
757
|
+
equal(res.statusCode, 400)
|
|
758
|
+
const json = res.json()
|
|
759
|
+
same(json.data, null)
|
|
760
|
+
same(json.errors[0].message, 'Cannot query field "hello" on type "Query".')
|
|
761
|
+
})
|
|
762
|
+
|
|
763
|
+
test('primary key snake_case', async ({ pass, teardown, same, equal }) => {
|
|
764
|
+
async function createBasicPagesWithSnakeCasePK (db, sql) {
|
|
765
|
+
if (isSQLite) {
|
|
766
|
+
await db.query(sql`CREATE TABLE pages (
|
|
767
|
+
page_id INTEGER PRIMARY KEY,
|
|
768
|
+
title VARCHAR(42)
|
|
769
|
+
);`)
|
|
770
|
+
} else {
|
|
771
|
+
await db.query(sql`CREATE TABLE pages (
|
|
772
|
+
page_id SERIAL PRIMARY KEY,
|
|
773
|
+
title VARCHAR(42)
|
|
774
|
+
);`)
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const app = fastify()
|
|
779
|
+
app.register(sqlMapper, {
|
|
780
|
+
...connInfo,
|
|
781
|
+
async onDatabaseLoad (db, sql) {
|
|
782
|
+
pass('onDatabaseLoad called')
|
|
783
|
+
|
|
784
|
+
await clear(db, sql)
|
|
785
|
+
await createBasicPagesWithSnakeCasePK(db, sql)
|
|
786
|
+
}
|
|
787
|
+
})
|
|
788
|
+
app.register(sqlGraphQL, {
|
|
789
|
+
graphiql: false
|
|
790
|
+
})
|
|
791
|
+
teardown(app.close.bind(app))
|
|
792
|
+
|
|
793
|
+
const res = await app.inject('/graphiql')
|
|
794
|
+
equal(res.statusCode, 404)
|
|
795
|
+
|
|
796
|
+
{
|
|
797
|
+
const res = await app.inject({
|
|
798
|
+
method: 'POST',
|
|
799
|
+
url: '/graphql',
|
|
800
|
+
body: {
|
|
801
|
+
query: `
|
|
802
|
+
mutation {
|
|
803
|
+
savePage(input: { title: "Hello" }) {
|
|
804
|
+
pageId
|
|
805
|
+
title
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
`
|
|
809
|
+
}
|
|
810
|
+
})
|
|
811
|
+
equal(res.statusCode, 200, 'savePage status code')
|
|
812
|
+
same(res.json(), {
|
|
813
|
+
data: {
|
|
814
|
+
savePage: {
|
|
815
|
+
pageId: 1,
|
|
816
|
+
title: 'Hello'
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}, 'savePage response')
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
{
|
|
823
|
+
const res = await app.inject({
|
|
824
|
+
method: 'POST',
|
|
825
|
+
url: '/graphql',
|
|
826
|
+
body: {
|
|
827
|
+
query: `
|
|
828
|
+
query {
|
|
829
|
+
getPageByPageId(pageId: 1) {
|
|
830
|
+
pageId
|
|
831
|
+
title
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
`
|
|
835
|
+
}
|
|
836
|
+
})
|
|
837
|
+
equal(res.statusCode, 200, 'pages status code')
|
|
838
|
+
same(res.json(), {
|
|
839
|
+
data: {
|
|
840
|
+
getPageByPageId: {
|
|
841
|
+
pageId: 1,
|
|
842
|
+
title: 'Hello'
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}, 'pages response')
|
|
846
|
+
}
|
|
847
|
+
})
|
|
848
|
+
|
|
849
|
+
test('deserialize JSON columns', { skip: isSQLite }, async (t) => {
|
|
850
|
+
const { pass, teardown, same } = t
|
|
851
|
+
const app = fastify()
|
|
852
|
+
const jsonData = {
|
|
853
|
+
foo: 'bar',
|
|
854
|
+
baz: 42,
|
|
855
|
+
items: ['foo', 'bar'],
|
|
856
|
+
nested: {
|
|
857
|
+
hello: 'world'
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
app.register(sqlMapper, {
|
|
861
|
+
...connInfo,
|
|
862
|
+
async onDatabaseLoad (db, sql) {
|
|
863
|
+
pass('onDatabaseLoad called')
|
|
864
|
+
|
|
865
|
+
await clear(db, sql)
|
|
866
|
+
|
|
867
|
+
await db.query(sql`CREATE TABLE pages (
|
|
868
|
+
id SERIAL PRIMARY KEY,
|
|
869
|
+
title VARCHAR(255) NOT NULL,
|
|
870
|
+
metadata JSON NOT NULL
|
|
871
|
+
);`)
|
|
872
|
+
|
|
873
|
+
await db.query(sql`INSERT INTO pages (id, title, metadata) VALUES (1, 'Hello World', ${JSON.stringify(jsonData)})`)
|
|
874
|
+
}
|
|
875
|
+
})
|
|
876
|
+
app.register(sqlGraphQL, {
|
|
877
|
+
graphiql: false
|
|
878
|
+
})
|
|
879
|
+
teardown(app.close.bind(app))
|
|
880
|
+
|
|
881
|
+
await app.ready()
|
|
882
|
+
const res = await app.inject({
|
|
883
|
+
method: 'POST',
|
|
884
|
+
url: '/graphql',
|
|
885
|
+
body: {
|
|
886
|
+
query: `
|
|
887
|
+
query {
|
|
888
|
+
getPageById(id: 1) {
|
|
889
|
+
id
|
|
890
|
+
title
|
|
891
|
+
metadata
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
`
|
|
895
|
+
}
|
|
896
|
+
})
|
|
897
|
+
const json = res.json()
|
|
898
|
+
if (isMariaDB) {
|
|
899
|
+
same(json.data.getPageById.metadata, JSON.stringify(jsonData))
|
|
900
|
+
} else {
|
|
901
|
+
same(json.data.getPageById.metadata, jsonData)
|
|
902
|
+
}
|
|
903
|
+
})
|