@platformatic/sql-graphql 0.10.0 → 0.12.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/index.js +15 -1
- package/lib/entity-to-type.js +17 -2
- package/lib/relationship.js +7 -7
- package/package.json +10 -10
- package/test/created_updated_at.test.js +517 -0
- package/test/enum.test.js +100 -0
- package/test/helper.js +50 -0
- package/test/inserted_updated_at.test.js +17 -1
- package/test/nested.test.js +155 -0
- package/test/schema.test.js +167 -0
- package/test/special-chars.test.js +47 -0
package/index.js
CHANGED
|
@@ -118,7 +118,21 @@ async function mapperToGraphql (app, opts) {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
let sdl =
|
|
121
|
+
let sdl = ''
|
|
122
|
+
try {
|
|
123
|
+
sdl = graphql.printSchema(new graphql.GraphQLSchema({ query, mutation, subscription }))
|
|
124
|
+
} catch (error) {
|
|
125
|
+
// The next lines are excluded from test coverage:
|
|
126
|
+
// it's quite hard to test the following lines for all of the DB types
|
|
127
|
+
/* istanbul ignore next */
|
|
128
|
+
app.log.debug({ query, mutation, subscription }, 'GraphQL input schema')
|
|
129
|
+
/* istanbul ignore next */
|
|
130
|
+
const newError = new Error('Error printing the GraphQL schema')
|
|
131
|
+
/* istanbul ignore next */
|
|
132
|
+
newError.cause = error
|
|
133
|
+
/* istanbul ignore next */
|
|
134
|
+
throw newError
|
|
135
|
+
}
|
|
122
136
|
|
|
123
137
|
if (opts.federationMetadata) {
|
|
124
138
|
for (const replacement of federationReplacements) {
|
package/lib/entity-to-type.js
CHANGED
|
@@ -50,10 +50,21 @@ function constructGraph (app, entity, opts, ignore) {
|
|
|
50
50
|
/* istanbul ignore next */
|
|
51
51
|
if (field.enum) {
|
|
52
52
|
const enumValues = field.enum.reduce((acc, enumValue) => {
|
|
53
|
-
|
|
53
|
+
const valueStartsWithLetterOrUnderscore = !!enumValue.match(/^[_a-zA-Z]/g)
|
|
54
|
+
if (!valueStartsWithLetterOrUnderscore) {
|
|
55
|
+
enumValue = `_${enumValue}`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
acc[enumValue.replace(/[^\w\s]/g, '_')] = { value: enumValue }
|
|
54
59
|
return acc
|
|
55
60
|
}, {})
|
|
56
|
-
|
|
61
|
+
try {
|
|
62
|
+
const name = camelcase([entityName, key])
|
|
63
|
+
meta.type = new graphql.GraphQLEnumType({ name, values: enumValues })
|
|
64
|
+
} catch (error) {
|
|
65
|
+
app.log.error({ key, enumValues, entityName, table: entity.table, schema: entity.schema })
|
|
66
|
+
throw new Error('Unable to generate GraphQLEnumType')
|
|
67
|
+
}
|
|
57
68
|
} else {
|
|
58
69
|
meta.type = sqlTypeToGraphQL(field.sqlType)
|
|
59
70
|
}
|
|
@@ -281,11 +292,14 @@ function constructGraph (app, entity, opts, ignore) {
|
|
|
281
292
|
async function loadMany (keys, queries, ctx) {
|
|
282
293
|
const fields = getFields(queries)
|
|
283
294
|
|
|
295
|
+
let limit = 0
|
|
296
|
+
|
|
284
297
|
// TODO this is inefficient as it might load
|
|
285
298
|
// more data than needed if there are more than
|
|
286
299
|
// one primary key
|
|
287
300
|
const where = keys.reduce((acc, pairs) => {
|
|
288
301
|
pairs.reduce((acc, { key, value }) => {
|
|
302
|
+
limit++
|
|
289
303
|
if (acc[key]) {
|
|
290
304
|
acc[key].in.push(value)
|
|
291
305
|
} else {
|
|
@@ -301,6 +315,7 @@ function constructGraph (app, entity, opts, ignore) {
|
|
|
301
315
|
const res = await entity.find({
|
|
302
316
|
where,
|
|
303
317
|
fields,
|
|
318
|
+
limit,
|
|
304
319
|
ctx
|
|
305
320
|
})
|
|
306
321
|
|
package/lib/relationship.js
CHANGED
|
@@ -9,20 +9,20 @@ const {
|
|
|
9
9
|
const assert = require('assert')
|
|
10
10
|
|
|
11
11
|
module.exports = function establishRelations (app, relations, resolvers, loaders, queryTopFields, relationships, metaMap) {
|
|
12
|
-
const
|
|
12
|
+
const entitiesTypeMap = {}
|
|
13
13
|
const entities = app.platformatic.entities
|
|
14
14
|
for (const key of Object.keys(entities)) {
|
|
15
|
-
|
|
16
|
-
tablesTypeMap[entity.table] = metaMap.get(entity)
|
|
15
|
+
entitiesTypeMap[key] = metaMap.get(entities[key])
|
|
17
16
|
}
|
|
18
|
-
for (const
|
|
17
|
+
for (const relation of relations) {
|
|
18
|
+
const { table_name, foreign_table_name, column_name, foreign_column_name, entityName, foreignEntityName, loweredTableWithSchemaName } = relation
|
|
19
19
|
const enhanceAssertLogMsg = `(table: "${table_name}", foreign table: "${foreign_table_name}", column: "${column_name}")`
|
|
20
20
|
|
|
21
21
|
assert(table_name, `table_name is required ${enhanceAssertLogMsg}`)
|
|
22
22
|
assert(foreign_table_name, `foreign_table_name is required ${enhanceAssertLogMsg}`)
|
|
23
23
|
|
|
24
|
-
const current =
|
|
25
|
-
const foreign =
|
|
24
|
+
const current = entitiesTypeMap[entityName]
|
|
25
|
+
const foreign = entitiesTypeMap[foreignEntityName]
|
|
26
26
|
assert(foreign !== undefined, `No foreign table named "${foreign_table_name}" was found ${enhanceAssertLogMsg}`)
|
|
27
27
|
|
|
28
28
|
// current to foreign, we skip this if the foreign table has a composite primary key
|
|
@@ -54,7 +54,7 @@ module.exports = function establishRelations (app, relations, resolvers, loaders
|
|
|
54
54
|
// foreign to current, we skip this if the current table has a composite primary key
|
|
55
55
|
// TODO implement support for this case
|
|
56
56
|
if (current.entity.primaryKeys.size === 1) {
|
|
57
|
-
const lowered =
|
|
57
|
+
const lowered = loweredTableWithSchemaName
|
|
58
58
|
if (!relationships[foreign.type] || relationships[foreign.type][lowered] !== false) {
|
|
59
59
|
foreign.fields[lowered] = queryTopFields[lowered]
|
|
60
60
|
resolvers[foreign.type] = resolvers[foreign.type] || {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/sql-graphql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Map SQL dbs to GraphQL",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -14,23 +14,23 @@
|
|
|
14
14
|
},
|
|
15
15
|
"homepage": "https://github.com/platformatic/platformatic#readme",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"
|
|
18
|
-
"@platformatic/sql-mapper": "0.10.0",
|
|
19
|
-
"fastify": "^4.6.0",
|
|
17
|
+
"fastify": "^4.10.2",
|
|
20
18
|
"snazzy": "^9.0.0",
|
|
21
19
|
"standard": "^17.0.0",
|
|
22
|
-
"tap": "^16.
|
|
20
|
+
"tap": "^16.3.2",
|
|
23
21
|
"tsd": "^0.25.0",
|
|
24
|
-
"ws": "^8.
|
|
22
|
+
"ws": "^8.11.0",
|
|
23
|
+
"@platformatic/sql-events": "0.12.0",
|
|
24
|
+
"@platformatic/sql-mapper": "0.12.0"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"camelcase": "^6.
|
|
28
|
-
"fastify-plugin": "^4.
|
|
27
|
+
"camelcase": "^6.3.0",
|
|
28
|
+
"fastify-plugin": "^4.4.0",
|
|
29
29
|
"graphql": "^16.6.0",
|
|
30
|
-
"graphql-scalars": "^1.
|
|
30
|
+
"graphql-scalars": "^1.20.1",
|
|
31
31
|
"graphql-type-json": "^0.3.2",
|
|
32
32
|
"inflected": "^2.1.0",
|
|
33
|
-
"mercurius": "^11.
|
|
33
|
+
"mercurius": "^11.4.0"
|
|
34
34
|
},
|
|
35
35
|
"tsd": {
|
|
36
36
|
"directory": "test/types"
|
|
@@ -0,0 +1,517 @@
|
|
|
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, isMysql } = require('./helper')
|
|
8
|
+
const { setTimeout } = require('timers/promises')
|
|
9
|
+
|
|
10
|
+
async function createBasicPages (db, sql) {
|
|
11
|
+
if (isSQLite) {
|
|
12
|
+
await db.query(sql`CREATE TABLE pages (
|
|
13
|
+
id INTEGER PRIMARY KEY,
|
|
14
|
+
title VARCHAR(42),
|
|
15
|
+
created_at TIMESTAMP,
|
|
16
|
+
updated_at TIMESTAMP
|
|
17
|
+
);`)
|
|
18
|
+
} else if (isMysql) {
|
|
19
|
+
await db.query(sql`CREATE TABLE pages (
|
|
20
|
+
id SERIAL PRIMARY KEY,
|
|
21
|
+
title VARCHAR(42),
|
|
22
|
+
created_at TIMESTAMP NULL DEFAULT NULL,
|
|
23
|
+
updated_at TIMESTAMP NULL DEFAULT NULL
|
|
24
|
+
);`)
|
|
25
|
+
} else {
|
|
26
|
+
await db.query(sql`CREATE TABLE pages (
|
|
27
|
+
id SERIAL PRIMARY KEY,
|
|
28
|
+
title VARCHAR(42),
|
|
29
|
+
created_at TIMESTAMP,
|
|
30
|
+
updated_at TIMESTAMP
|
|
31
|
+
);`)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test('created_at updated_at happy path', async ({ pass, teardown, same, equal, not, comment }) => {
|
|
36
|
+
const app = fastify()
|
|
37
|
+
app.register(sqlMapper, {
|
|
38
|
+
...connInfo,
|
|
39
|
+
async onDatabaseLoad (db, sql) {
|
|
40
|
+
pass('onDatabaseLoad called')
|
|
41
|
+
|
|
42
|
+
await clear(db, sql)
|
|
43
|
+
await createBasicPages(db, sql)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
app.register(sqlGraphQL)
|
|
47
|
+
teardown(app.close.bind(app))
|
|
48
|
+
|
|
49
|
+
await app.ready()
|
|
50
|
+
|
|
51
|
+
let original
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
const res = await app.inject({
|
|
55
|
+
method: 'POST',
|
|
56
|
+
url: '/graphql',
|
|
57
|
+
body: {
|
|
58
|
+
query: `
|
|
59
|
+
mutation {
|
|
60
|
+
savePage(input: { title: "Hello" }) {
|
|
61
|
+
id
|
|
62
|
+
title
|
|
63
|
+
createdAt
|
|
64
|
+
updatedAt
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
equal(res.statusCode, 200, 'savePage status code')
|
|
71
|
+
const data = res.json().data
|
|
72
|
+
not(data.savePage.createdAt, null, 'createdAt')
|
|
73
|
+
not(data.savePage.updatedAt, null, 'updatedAt')
|
|
74
|
+
comment(`createdAt: ${data.savePage.createdAt}`)
|
|
75
|
+
comment(`updatedAt: ${data.savePage.updatedAt}`)
|
|
76
|
+
original = data.savePage
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
const res = await app.inject({
|
|
81
|
+
method: 'POST',
|
|
82
|
+
url: '/graphql',
|
|
83
|
+
body: {
|
|
84
|
+
query: `
|
|
85
|
+
query {
|
|
86
|
+
getPageById(id: 1) {
|
|
87
|
+
id
|
|
88
|
+
title
|
|
89
|
+
createdAt
|
|
90
|
+
updatedAt
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
equal(res.statusCode, 200, 'pages status code')
|
|
97
|
+
const data = res.json().data
|
|
98
|
+
equal(data.getPageById.createdAt, original.createdAt, 'createdAt')
|
|
99
|
+
equal(data.getPageById.updatedAt, original.updatedAt, 'updatedAt')
|
|
100
|
+
comment(`createdAt: ${data.getPageById.createdAt}`)
|
|
101
|
+
comment(`updatedAt: ${data.getPageById.updatedAt}`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await setTimeout(1000) // await 1s
|
|
105
|
+
|
|
106
|
+
let updated
|
|
107
|
+
{
|
|
108
|
+
const res = await app.inject({
|
|
109
|
+
method: 'POST',
|
|
110
|
+
url: '/graphql',
|
|
111
|
+
body: {
|
|
112
|
+
query: `
|
|
113
|
+
mutation {
|
|
114
|
+
savePage(input: { id: 1, title: "Hello World" }) {
|
|
115
|
+
id
|
|
116
|
+
title
|
|
117
|
+
createdAt
|
|
118
|
+
updatedAt
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
`
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
const data = res.json().data
|
|
125
|
+
equal(data.savePage.createdAt, original.createdAt, 'createdAt')
|
|
126
|
+
not(data.savePage.updatedAt, original.updatedAt, 'updatedAt')
|
|
127
|
+
updated = data.savePage
|
|
128
|
+
comment(`createdAt: ${data.savePage.createdAt}`)
|
|
129
|
+
comment(`updatedAt: ${data.savePage.updatedAt}`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
const res = await app.inject({
|
|
134
|
+
method: 'POST',
|
|
135
|
+
url: '/graphql',
|
|
136
|
+
body: {
|
|
137
|
+
query: `
|
|
138
|
+
query {
|
|
139
|
+
getPageById(id: 1) {
|
|
140
|
+
id
|
|
141
|
+
title
|
|
142
|
+
createdAt
|
|
143
|
+
updatedAt
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
`
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
equal(res.statusCode, 200, 'pages status code')
|
|
150
|
+
const data = res.json().data
|
|
151
|
+
equal(data.getPageById.createdAt, updated.createdAt, 'createdAt')
|
|
152
|
+
equal(data.getPageById.updatedAt, updated.updatedAt, 'updatedAt')
|
|
153
|
+
comment(`createdAt: ${data.getPageById.createdAt}`)
|
|
154
|
+
comment(`updatedAt: ${data.getPageById.updatedAt}`)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('cannot set created_at', async ({ pass, teardown, same, equal, not, comment }) => {
|
|
159
|
+
const app = fastify()
|
|
160
|
+
app.register(sqlMapper, {
|
|
161
|
+
...connInfo,
|
|
162
|
+
async onDatabaseLoad (db, sql) {
|
|
163
|
+
pass('onDatabaseLoad called')
|
|
164
|
+
|
|
165
|
+
await clear(db, sql)
|
|
166
|
+
await createBasicPages(db, sql)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
app.register(sqlGraphQL)
|
|
170
|
+
teardown(app.close.bind(app))
|
|
171
|
+
|
|
172
|
+
await app.ready()
|
|
173
|
+
|
|
174
|
+
{
|
|
175
|
+
const res = await app.inject({
|
|
176
|
+
method: 'POST',
|
|
177
|
+
url: '/graphql',
|
|
178
|
+
body: {
|
|
179
|
+
query: `
|
|
180
|
+
mutation {
|
|
181
|
+
savePage(input: { title: "Hello", createdAt: "${new Date().toISOString()}" }) {
|
|
182
|
+
id
|
|
183
|
+
title
|
|
184
|
+
createdAt
|
|
185
|
+
updatedAt
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
`
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
equal(res.statusCode, 400, 'savePage status code')
|
|
192
|
+
const data = res.json()
|
|
193
|
+
equal(data.errors[0].message, 'Field "createdAt" is not defined by type "PageInput".')
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
test('cannot set updated_at', async ({ pass, teardown, same, equal, not, comment }) => {
|
|
198
|
+
const app = fastify()
|
|
199
|
+
app.register(sqlMapper, {
|
|
200
|
+
...connInfo,
|
|
201
|
+
async onDatabaseLoad (db, sql) {
|
|
202
|
+
pass('onDatabaseLoad called')
|
|
203
|
+
|
|
204
|
+
await clear(db, sql)
|
|
205
|
+
await createBasicPages(db, sql)
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
app.register(sqlGraphQL)
|
|
209
|
+
teardown(app.close.bind(app))
|
|
210
|
+
|
|
211
|
+
await app.ready()
|
|
212
|
+
|
|
213
|
+
{
|
|
214
|
+
const res = await app.inject({
|
|
215
|
+
method: 'POST',
|
|
216
|
+
url: '/graphql',
|
|
217
|
+
body: {
|
|
218
|
+
query: `
|
|
219
|
+
mutation {
|
|
220
|
+
savePage(input: { title: "Hello" }) {
|
|
221
|
+
id
|
|
222
|
+
title
|
|
223
|
+
createdAt
|
|
224
|
+
updatedAt
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
`
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
equal(res.statusCode, 200, 'savePage status code')
|
|
231
|
+
const data = res.json().data
|
|
232
|
+
not(data.savePage.createdAt, null, 'createdAt')
|
|
233
|
+
not(data.savePage.updatedAt, null, 'updatedAt')
|
|
234
|
+
comment(`createdAt: ${data.savePage.createdAt}`)
|
|
235
|
+
comment(`updatedAt: ${data.savePage.updatedAt}`)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
{
|
|
239
|
+
const res = await app.inject({
|
|
240
|
+
method: 'POST',
|
|
241
|
+
url: '/graphql',
|
|
242
|
+
body: {
|
|
243
|
+
query: `
|
|
244
|
+
mutation {
|
|
245
|
+
savePage(input: { id: 1, title: "Hello World", updatedAt: "${new Date().toISOString()}" }) {
|
|
246
|
+
id
|
|
247
|
+
title
|
|
248
|
+
createdAt
|
|
249
|
+
updatedAt
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
`
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
equal(res.statusCode, 400, 'savePage status code')
|
|
256
|
+
const data = res.json()
|
|
257
|
+
equal(data.errors[0].message, 'Field "updatedAt" is not defined by type "PageInput".')
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
test('do not assign created_at updated_at', async ({ pass, teardown, same, equal, not, comment }) => {
|
|
262
|
+
const app = fastify()
|
|
263
|
+
app.register(sqlMapper, {
|
|
264
|
+
...connInfo,
|
|
265
|
+
autoTimestamp: false,
|
|
266
|
+
async onDatabaseLoad (db, sql) {
|
|
267
|
+
pass('onDatabaseLoad called')
|
|
268
|
+
|
|
269
|
+
await clear(db, sql)
|
|
270
|
+
await createBasicPages(db, sql)
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
app.register(sqlGraphQL)
|
|
274
|
+
teardown(app.close.bind(app))
|
|
275
|
+
|
|
276
|
+
await app.ready()
|
|
277
|
+
|
|
278
|
+
{
|
|
279
|
+
const res = await app.inject({
|
|
280
|
+
method: 'POST',
|
|
281
|
+
url: '/graphql',
|
|
282
|
+
body: {
|
|
283
|
+
query: `
|
|
284
|
+
mutation {
|
|
285
|
+
savePage(input: { title: "Hello" }) {
|
|
286
|
+
id
|
|
287
|
+
title
|
|
288
|
+
createdAt
|
|
289
|
+
updatedAt
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
`
|
|
293
|
+
}
|
|
294
|
+
})
|
|
295
|
+
equal(res.statusCode, 200, 'savePage status code')
|
|
296
|
+
const data = res.json().data
|
|
297
|
+
equal(data.savePage.createdAt, null, 'createdAt')
|
|
298
|
+
equal(data.savePage.updatedAt, null, 'updatedAt')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
{
|
|
302
|
+
const res = await app.inject({
|
|
303
|
+
method: 'POST',
|
|
304
|
+
url: '/graphql',
|
|
305
|
+
body: {
|
|
306
|
+
query: `
|
|
307
|
+
query {
|
|
308
|
+
getPageById(id: 1) {
|
|
309
|
+
id
|
|
310
|
+
title
|
|
311
|
+
createdAt
|
|
312
|
+
updatedAt
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
`
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
equal(res.statusCode, 200, 'pages status code')
|
|
319
|
+
const data = res.json().data
|
|
320
|
+
equal(data.getPageById.createdAt, null, 'createdAt')
|
|
321
|
+
equal(data.getPageById.updatedAt, null, 'updatedAt')
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
{
|
|
325
|
+
const res = await app.inject({
|
|
326
|
+
method: 'POST',
|
|
327
|
+
url: '/graphql',
|
|
328
|
+
body: {
|
|
329
|
+
query: `
|
|
330
|
+
mutation {
|
|
331
|
+
savePage(input: { id: 1, title: "Hello World" }) {
|
|
332
|
+
id
|
|
333
|
+
title
|
|
334
|
+
createdAt
|
|
335
|
+
updatedAt
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
`
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
const data = res.json().data
|
|
342
|
+
equal(data.savePage.createdAt, null, 'createdAt')
|
|
343
|
+
equal(data.savePage.updatedAt, null, 'updatedAt')
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
{
|
|
347
|
+
const res = await app.inject({
|
|
348
|
+
method: 'POST',
|
|
349
|
+
url: '/graphql',
|
|
350
|
+
body: {
|
|
351
|
+
query: `
|
|
352
|
+
query {
|
|
353
|
+
getPageById(id: 1) {
|
|
354
|
+
id
|
|
355
|
+
title
|
|
356
|
+
createdAt
|
|
357
|
+
updatedAt
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
`
|
|
361
|
+
}
|
|
362
|
+
})
|
|
363
|
+
equal(res.statusCode, 200, 'pages status code')
|
|
364
|
+
const data = res.json().data
|
|
365
|
+
equal(data.getPageById.createdAt, null, 'createdAt')
|
|
366
|
+
equal(data.getPageById.updatedAt, null, 'updatedAt')
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
test('bulk insert adds created_at updated_at', async ({ pass, teardown, same, equal, not, comment }) => {
|
|
371
|
+
const app = fastify()
|
|
372
|
+
app.register(sqlMapper, {
|
|
373
|
+
...connInfo,
|
|
374
|
+
async onDatabaseLoad (db, sql) {
|
|
375
|
+
pass('onDatabaseLoad called')
|
|
376
|
+
|
|
377
|
+
await clear(db, sql)
|
|
378
|
+
await createBasicPages(db, sql)
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
app.register(sqlGraphQL)
|
|
382
|
+
teardown(app.close.bind(app))
|
|
383
|
+
|
|
384
|
+
await app.ready()
|
|
385
|
+
|
|
386
|
+
{
|
|
387
|
+
const res = await app.inject({
|
|
388
|
+
method: 'POST',
|
|
389
|
+
url: '/graphql',
|
|
390
|
+
body: {
|
|
391
|
+
query: `
|
|
392
|
+
mutation {
|
|
393
|
+
savePage(input: { title: "Hello" }) {
|
|
394
|
+
id
|
|
395
|
+
title
|
|
396
|
+
createdAt
|
|
397
|
+
updatedAt
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
`
|
|
401
|
+
}
|
|
402
|
+
})
|
|
403
|
+
equal(res.statusCode, 200, 'savePage status code')
|
|
404
|
+
const data = res.json().data
|
|
405
|
+
not(data.savePage.createdAt, null, 'createdAt')
|
|
406
|
+
not(data.savePage.updatedAt, null, 'updatedAt')
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
{
|
|
410
|
+
const res = await app.inject({
|
|
411
|
+
method: 'POST',
|
|
412
|
+
url: '/graphql',
|
|
413
|
+
body: {
|
|
414
|
+
query: `
|
|
415
|
+
mutation batch($inputs : [PageInput]!) {
|
|
416
|
+
insertPages (inputs: $inputs) {
|
|
417
|
+
id
|
|
418
|
+
title
|
|
419
|
+
createdAt
|
|
420
|
+
updatedAt
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
`,
|
|
424
|
+
variables: {
|
|
425
|
+
inputs: [
|
|
426
|
+
{ title: 'Page 1' },
|
|
427
|
+
{ title: 'Page 2' },
|
|
428
|
+
{ title: 'Page 3' }
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
})
|
|
433
|
+
equal(res.statusCode, 200, 'savePage status code')
|
|
434
|
+
const pages = res.json().data.insertPages
|
|
435
|
+
for (const page of pages) {
|
|
436
|
+
not(page.createdAt, null, 'createdAt')
|
|
437
|
+
not(page.updatedAt, null, 'updatedAt')
|
|
438
|
+
equal(page.createdAt, page.updatedAt, 'createdAt === updatedAt')
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
{
|
|
443
|
+
const res = await app.inject({
|
|
444
|
+
method: 'POST',
|
|
445
|
+
url: '/graphql',
|
|
446
|
+
body: {
|
|
447
|
+
query: `
|
|
448
|
+
query {
|
|
449
|
+
pages {
|
|
450
|
+
id
|
|
451
|
+
title
|
|
452
|
+
createdAt
|
|
453
|
+
updatedAt
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
`
|
|
457
|
+
}
|
|
458
|
+
})
|
|
459
|
+
equal(res.statusCode, 200, 'pages status code')
|
|
460
|
+
const pages = res.json().data.pages
|
|
461
|
+
for (const page of pages) {
|
|
462
|
+
not(page.createdAt, null, 'createdAt')
|
|
463
|
+
not(page.updatedAt, null, 'updatedAt')
|
|
464
|
+
equal(page.createdAt, page.updatedAt, 'createdAt === updatedAt')
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
test('bulk insert with autoTimestamp=false do not had created_at updated_at', async ({ pass, teardown, same, equal, not, comment }) => {
|
|
470
|
+
const app = fastify()
|
|
471
|
+
app.register(sqlMapper, {
|
|
472
|
+
...connInfo,
|
|
473
|
+
autoTimestamp: false,
|
|
474
|
+
async onDatabaseLoad (db, sql) {
|
|
475
|
+
pass('onDatabaseLoad called')
|
|
476
|
+
|
|
477
|
+
await clear(db, sql)
|
|
478
|
+
await createBasicPages(db, sql)
|
|
479
|
+
}
|
|
480
|
+
})
|
|
481
|
+
app.register(sqlGraphQL)
|
|
482
|
+
teardown(app.close.bind(app))
|
|
483
|
+
|
|
484
|
+
await app.ready()
|
|
485
|
+
|
|
486
|
+
{
|
|
487
|
+
const res = await app.inject({
|
|
488
|
+
method: 'POST',
|
|
489
|
+
url: '/graphql',
|
|
490
|
+
body: {
|
|
491
|
+
query: `
|
|
492
|
+
mutation batch($inputs : [PageInput]!) {
|
|
493
|
+
insertPages (inputs: $inputs) {
|
|
494
|
+
id
|
|
495
|
+
title
|
|
496
|
+
createdAt
|
|
497
|
+
updatedAt
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
`,
|
|
501
|
+
variables: {
|
|
502
|
+
inputs: [
|
|
503
|
+
{ title: 'Page 1' },
|
|
504
|
+
{ title: 'Page 2' },
|
|
505
|
+
{ title: 'Page 3' }
|
|
506
|
+
]
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
})
|
|
510
|
+
equal(res.statusCode, 200, 'savePage status code')
|
|
511
|
+
const pages = res.json().data.insertPages
|
|
512
|
+
for (const page of pages) {
|
|
513
|
+
equal(page.createdAt, null, 'createdAt')
|
|
514
|
+
equal(page.updatedAt, null, 'updatedAt')
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
})
|
|
@@ -0,0 +1,100 @@
|
|
|
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, isPg } = require('./helper')
|
|
8
|
+
|
|
9
|
+
test('should properly setup the enum types', { skip: isSQLite }, async ({ pass, teardown, same }) => {
|
|
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 (isPg) {
|
|
19
|
+
await db.query(sql`
|
|
20
|
+
CREATE TYPE custom_enum AS ENUM('1', '2', '3');
|
|
21
|
+
|
|
22
|
+
CREATE TABLE enum_tests (
|
|
23
|
+
id INTEGER NOT NULL,
|
|
24
|
+
test_enum custom_enum,
|
|
25
|
+
PRIMARY KEY (id)
|
|
26
|
+
);`)
|
|
27
|
+
} else {
|
|
28
|
+
await db.query(sql`
|
|
29
|
+
CREATE TABLE enum_tests (
|
|
30
|
+
id INTEGER NOT NULL,
|
|
31
|
+
test_enum ENUM ('1', '2', '3') DEFAULT NULL,
|
|
32
|
+
PRIMARY KEY (id)
|
|
33
|
+
);`)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
app.register(sqlGraphQL)
|
|
39
|
+
teardown(app.close.bind(app))
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
await app.ready()
|
|
43
|
+
same(true, true, 'Enum values are properly interpreted')
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.log(err)
|
|
46
|
+
same(true, false, 'Previous call should never fail')
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('should not fail if tables have duplicate enum names', { skip: isSQLite }, async ({ pass, teardown, same }) => {
|
|
51
|
+
const app = fastify()
|
|
52
|
+
app.register(sqlMapper, {
|
|
53
|
+
...connInfo,
|
|
54
|
+
async onDatabaseLoad (db, sql) {
|
|
55
|
+
pass('onDatabaseLoad called')
|
|
56
|
+
|
|
57
|
+
await clear(db, sql)
|
|
58
|
+
|
|
59
|
+
if (isPg) {
|
|
60
|
+
await db.query(sql`
|
|
61
|
+
CREATE TYPE custom_enum AS ENUM('1', '2', '3');
|
|
62
|
+
CREATE TYPE simple_enum AS ENUM('4', '5', '6');
|
|
63
|
+
|
|
64
|
+
CREATE TABLE enum_tests (
|
|
65
|
+
id INTEGER NOT NULL,
|
|
66
|
+
test_enum custom_enum,
|
|
67
|
+
PRIMARY KEY (id)
|
|
68
|
+
);
|
|
69
|
+
CREATE TABLE simple_types (
|
|
70
|
+
pk INTEGER NOT NULL,
|
|
71
|
+
test_enum simple_enum,
|
|
72
|
+
PRIMARY KEY (pk)
|
|
73
|
+
);`)
|
|
74
|
+
} else {
|
|
75
|
+
await db.query(sql`
|
|
76
|
+
CREATE TABLE enum_tests (
|
|
77
|
+
id INTEGER NOT NULL,
|
|
78
|
+
test_enum ENUM ('1', '2', '3') DEFAULT NULL,
|
|
79
|
+
PRIMARY KEY (id)
|
|
80
|
+
);
|
|
81
|
+
CREATE TABLE simple_types (
|
|
82
|
+
pk INTEGER NOT NULL,
|
|
83
|
+
test_enum ENUM ('4', '5', '6') DEFAULT NULL,
|
|
84
|
+
PRIMARY KEY (pk)
|
|
85
|
+
);`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
app.register(sqlGraphQL)
|
|
91
|
+
teardown(app.close.bind(app))
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
await app.ready()
|
|
95
|
+
same(true, true, 'test_enum is used twice but app not throws')
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.log(err)
|
|
98
|
+
same(true, false, 'Previous call should never fail')
|
|
99
|
+
}
|
|
100
|
+
})
|
package/test/helper.js
CHANGED
|
@@ -96,4 +96,54 @@ module.exports.clear = async function (db, sql) {
|
|
|
96
96
|
await db.query(sql`DROP Type simple_enum`)
|
|
97
97
|
} catch (err) {
|
|
98
98
|
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
await db.query(sql`DROP TABLE "myschema"."mytable"`)
|
|
102
|
+
} catch (err) {
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
await db.query(sql`DROP TABLE "myschema"."template"`)
|
|
107
|
+
} catch (err) {
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await db.query(sql`DROP SCHEMA "myschema"`)
|
|
112
|
+
} catch (err) {
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await db.query(sql`DROP TABLE generated_test`)
|
|
117
|
+
} catch (err) {
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
await db.query(sql`DROP TABLE enum_tests`)
|
|
122
|
+
} catch (err) {
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
await db.query(sql`DROP TYPE custom_enum`)
|
|
127
|
+
} catch (err) {
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
await db.query(sql`DROP TABLE test2.books`)
|
|
132
|
+
} catch (err) {
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
await db.query(sql`DROP TABLE test1.authors`)
|
|
137
|
+
} catch (err) {
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
await db.query(sql`DROP SCHEMA test2`)
|
|
142
|
+
} catch (err) {
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await db.query(sql`DROP SCHEMA test1`)
|
|
147
|
+
} catch (err) {
|
|
148
|
+
}
|
|
99
149
|
}
|
|
@@ -36,6 +36,10 @@ test('inserted_at updated_at happy path', async ({ pass, teardown, same, equal,
|
|
|
36
36
|
const app = fastify()
|
|
37
37
|
app.register(sqlMapper, {
|
|
38
38
|
...connInfo,
|
|
39
|
+
autoTimestamp: {
|
|
40
|
+
createdAt: 'inserted_at',
|
|
41
|
+
updatedAt: 'updated_at'
|
|
42
|
+
},
|
|
39
43
|
async onDatabaseLoad (db, sql) {
|
|
40
44
|
pass('onDatabaseLoad called')
|
|
41
45
|
|
|
@@ -159,6 +163,10 @@ test('cannot set inserted_at', async ({ pass, teardown, same, equal, not, commen
|
|
|
159
163
|
const app = fastify()
|
|
160
164
|
app.register(sqlMapper, {
|
|
161
165
|
...connInfo,
|
|
166
|
+
autoTimestamp: {
|
|
167
|
+
createdAt: 'inserted_at',
|
|
168
|
+
updatedAt: 'updated_at'
|
|
169
|
+
},
|
|
162
170
|
async onDatabaseLoad (db, sql) {
|
|
163
171
|
pass('onDatabaseLoad called')
|
|
164
172
|
|
|
@@ -198,6 +206,10 @@ test('cannot set updated_at', async ({ pass, teardown, same, equal, not, comment
|
|
|
198
206
|
const app = fastify()
|
|
199
207
|
app.register(sqlMapper, {
|
|
200
208
|
...connInfo,
|
|
209
|
+
autoTimestamp: {
|
|
210
|
+
createdAt: 'inserted_at',
|
|
211
|
+
updatedAt: 'updated_at'
|
|
212
|
+
},
|
|
201
213
|
async onDatabaseLoad (db, sql) {
|
|
202
214
|
pass('onDatabaseLoad called')
|
|
203
215
|
|
|
@@ -371,6 +383,10 @@ test('bulk insert adds inserted_at updated_at', async ({ pass, teardown, same, e
|
|
|
371
383
|
const app = fastify()
|
|
372
384
|
app.register(sqlMapper, {
|
|
373
385
|
...connInfo,
|
|
386
|
+
autoTimestamp: {
|
|
387
|
+
createdAt: 'inserted_at',
|
|
388
|
+
updatedAt: 'updated_at'
|
|
389
|
+
},
|
|
374
390
|
async onDatabaseLoad (db, sql) {
|
|
375
391
|
pass('onDatabaseLoad called')
|
|
376
392
|
|
|
@@ -466,7 +482,7 @@ test('bulk insert adds inserted_at updated_at', async ({ pass, teardown, same, e
|
|
|
466
482
|
}
|
|
467
483
|
})
|
|
468
484
|
|
|
469
|
-
test('bulk insert with autoTimestamp=false do not
|
|
485
|
+
test('bulk insert with autoTimestamp=false do not had inserted_at updated_at', async ({ pass, teardown, same, equal, not, comment }) => {
|
|
470
486
|
const app = fastify()
|
|
471
487
|
app.register(sqlMapper, {
|
|
472
488
|
...connInfo,
|
package/test/nested.test.js
CHANGED
|
@@ -258,6 +258,161 @@ test('nested resolver', async ({ pass, teardown, same, equal }) => {
|
|
|
258
258
|
}
|
|
259
259
|
})
|
|
260
260
|
|
|
261
|
+
test('nested resolver with more of 10 rows in nested entity', async ({ pass, teardown, same, equal }) => {
|
|
262
|
+
const app = fastify()
|
|
263
|
+
app.register(sqlMapper, {
|
|
264
|
+
...connInfo,
|
|
265
|
+
async onDatabaseLoad (db, sql) {
|
|
266
|
+
pass('onDatabaseLoad called')
|
|
267
|
+
|
|
268
|
+
await clear(db, sql)
|
|
269
|
+
|
|
270
|
+
if (isMysql) {
|
|
271
|
+
await db.query(sql`
|
|
272
|
+
CREATE TABLE categories (
|
|
273
|
+
id SERIAL PRIMARY KEY,
|
|
274
|
+
name VARCHAR(255)
|
|
275
|
+
);
|
|
276
|
+
CREATE TABLE pages (
|
|
277
|
+
id SERIAL PRIMARY KEY,
|
|
278
|
+
title VARCHAR(42),
|
|
279
|
+
category_id BIGINT UNSIGNED,
|
|
280
|
+
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE
|
|
281
|
+
);
|
|
282
|
+
`)
|
|
283
|
+
} else if (isSQLite) {
|
|
284
|
+
await db.query(sql`
|
|
285
|
+
CREATE TABLE categories (
|
|
286
|
+
id INTEGER PRIMARY KEY,
|
|
287
|
+
name VARCHAR(42)
|
|
288
|
+
);
|
|
289
|
+
`)
|
|
290
|
+
await db.query(sql`
|
|
291
|
+
CREATE TABLE pages (
|
|
292
|
+
id INTEGER PRIMARY KEY,
|
|
293
|
+
title VARCHAR(42),
|
|
294
|
+
category_id INTEGER REFERENCES categories(id)
|
|
295
|
+
);
|
|
296
|
+
`)
|
|
297
|
+
} else {
|
|
298
|
+
await db.query(sql`
|
|
299
|
+
CREATE TABLE categories (
|
|
300
|
+
id SERIAL PRIMARY KEY,
|
|
301
|
+
name VARCHAR(42)
|
|
302
|
+
);
|
|
303
|
+
CREATE TABLE pages (
|
|
304
|
+
id SERIAL PRIMARY KEY,
|
|
305
|
+
title VARCHAR(42),
|
|
306
|
+
category_id INTEGER REFERENCES categories(id)
|
|
307
|
+
);
|
|
308
|
+
`)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
app.register(sqlGraphQL)
|
|
313
|
+
teardown(app.close.bind(app))
|
|
314
|
+
|
|
315
|
+
const categories = [
|
|
316
|
+
{ name: 'Category 01' },
|
|
317
|
+
{ name: 'Category 02' },
|
|
318
|
+
{ name: 'Category 03' },
|
|
319
|
+
{ name: 'Category 04' },
|
|
320
|
+
{ name: 'Category 05' },
|
|
321
|
+
{ name: 'Category 06' },
|
|
322
|
+
{ name: 'Category 07' },
|
|
323
|
+
{ name: 'Category 08' },
|
|
324
|
+
{ name: 'Category 09' },
|
|
325
|
+
{ name: 'Category 10' },
|
|
326
|
+
{ name: 'Category 11' }
|
|
327
|
+
]
|
|
328
|
+
await app.inject({
|
|
329
|
+
method: 'POST',
|
|
330
|
+
url: '/graphql',
|
|
331
|
+
body: {
|
|
332
|
+
query: `
|
|
333
|
+
mutation batch($inputs: [CategoryInput]!) {
|
|
334
|
+
insertCategories(inputs: $inputs) {
|
|
335
|
+
id
|
|
336
|
+
name
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
`,
|
|
340
|
+
variables: {
|
|
341
|
+
inputs: categories
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
const pages = [
|
|
347
|
+
{ title: 'Page 01', categoryId: 1 },
|
|
348
|
+
{ title: 'Page 02', categoryId: 2 },
|
|
349
|
+
{ title: 'Page 03', categoryId: 3 },
|
|
350
|
+
{ title: 'Page 04', categoryId: 4 },
|
|
351
|
+
{ title: 'Page 05', categoryId: 5 },
|
|
352
|
+
{ title: 'Page 06', categoryId: 6 },
|
|
353
|
+
{ title: 'Page 07', categoryId: 7 },
|
|
354
|
+
{ title: 'Page 08', categoryId: 8 },
|
|
355
|
+
{ title: 'Page 09', categoryId: 9 },
|
|
356
|
+
{ title: 'Page 10', categoryId: 10 },
|
|
357
|
+
{ title: 'Page 11', categoryId: 11 }
|
|
358
|
+
]
|
|
359
|
+
await app.inject({
|
|
360
|
+
method: 'POST',
|
|
361
|
+
url: '/graphql',
|
|
362
|
+
body: {
|
|
363
|
+
query: `
|
|
364
|
+
mutation batch($inputs: [PageInput]!) {
|
|
365
|
+
insertPages(inputs: $inputs) {
|
|
366
|
+
id
|
|
367
|
+
title
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
`,
|
|
371
|
+
variables: {
|
|
372
|
+
inputs: pages
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
{
|
|
378
|
+
const res = await app.inject({
|
|
379
|
+
method: 'POST',
|
|
380
|
+
url: '/graphql',
|
|
381
|
+
body: {
|
|
382
|
+
query: `
|
|
383
|
+
query {
|
|
384
|
+
pages(limit: 100) {
|
|
385
|
+
id
|
|
386
|
+
title
|
|
387
|
+
category {
|
|
388
|
+
id
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
`
|
|
393
|
+
}
|
|
394
|
+
})
|
|
395
|
+
equal(res.statusCode, 200, 'pages.category status code')
|
|
396
|
+
same(res.json(), {
|
|
397
|
+
data: {
|
|
398
|
+
pages: [
|
|
399
|
+
{ id: 1, title: 'Page 01', category: { id: 1 } },
|
|
400
|
+
{ id: 2, title: 'Page 02', category: { id: 2 } },
|
|
401
|
+
{ id: 3, title: 'Page 03', category: { id: 3 } },
|
|
402
|
+
{ id: 4, title: 'Page 04', category: { id: 4 } },
|
|
403
|
+
{ id: 5, title: 'Page 05', category: { id: 5 } },
|
|
404
|
+
{ id: 6, title: 'Page 06', category: { id: 6 } },
|
|
405
|
+
{ id: 7, title: 'Page 07', category: { id: 7 } },
|
|
406
|
+
{ id: 8, title: 'Page 08', category: { id: 8 } },
|
|
407
|
+
{ id: 9, title: 'Page 09', category: { id: 9 } },
|
|
408
|
+
{ id: 10, title: 'Page 10', category: { id: 10 } },
|
|
409
|
+
{ id: 11, title: 'Page 11', category: { id: 11 } }
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
}, 'pages.category response')
|
|
413
|
+
}
|
|
414
|
+
})
|
|
415
|
+
|
|
261
416
|
test('disable one-too-many', async ({ pass, teardown, same, equal, match }) => {
|
|
262
417
|
const app = fastify()
|
|
263
418
|
app.register(sqlMapper, {
|
|
@@ -0,0 +1,167 @@
|
|
|
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 { isSQLite, connInfo, isMysql, clear } = require('./helper')
|
|
8
|
+
|
|
9
|
+
test('should handle relationships with different schemas', { skip: isSQLite }, async ({ pass, teardown, same, equal }) => {
|
|
10
|
+
const app = fastify()
|
|
11
|
+
app.register(sqlMapper, {
|
|
12
|
+
...connInfo,
|
|
13
|
+
schema: ['test1', 'test2'],
|
|
14
|
+
async onDatabaseLoad (db, sql) {
|
|
15
|
+
pass('onDatabaseLoad called')
|
|
16
|
+
|
|
17
|
+
await clear(db, sql)
|
|
18
|
+
|
|
19
|
+
await db.query(sql`CREATE SCHEMA IF NOT EXISTS test1;`)
|
|
20
|
+
await db.query(sql`CREATE SCHEMA IF NOT EXISTS test2;`)
|
|
21
|
+
if (isMysql) {
|
|
22
|
+
await db.query(sql`
|
|
23
|
+
CREATE TABLE test1.authors (
|
|
24
|
+
id SERIAL PRIMARY KEY,
|
|
25
|
+
name VARCHAR(255)
|
|
26
|
+
);`)
|
|
27
|
+
await db.query(sql`
|
|
28
|
+
CREATE TABLE test2.books (
|
|
29
|
+
id SERIAL PRIMARY KEY,
|
|
30
|
+
title VARCHAR(42),
|
|
31
|
+
author_id BIGINT UNSIGNED,
|
|
32
|
+
FOREIGN KEY(author_id) REFERENCES test1.authors(id) ON DELETE CASCADE
|
|
33
|
+
);
|
|
34
|
+
`)
|
|
35
|
+
} else {
|
|
36
|
+
await db.query(sql`
|
|
37
|
+
CREATE TABLE test1.authors (
|
|
38
|
+
id SERIAL PRIMARY KEY,
|
|
39
|
+
name VARCHAR(42)
|
|
40
|
+
);
|
|
41
|
+
CREATE TABLE test2.books (
|
|
42
|
+
id SERIAL PRIMARY KEY,
|
|
43
|
+
title VARCHAR(42),
|
|
44
|
+
author_id INTEGER REFERENCES test1.authors(id)
|
|
45
|
+
);
|
|
46
|
+
`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
app.register(sqlGraphQL)
|
|
51
|
+
teardown(app.close.bind(app))
|
|
52
|
+
|
|
53
|
+
await app.ready()
|
|
54
|
+
|
|
55
|
+
const authors = [{
|
|
56
|
+
id: 1,
|
|
57
|
+
name: 'Mark'
|
|
58
|
+
}]
|
|
59
|
+
|
|
60
|
+
const books = [{
|
|
61
|
+
id: 1,
|
|
62
|
+
title: 'Harry',
|
|
63
|
+
authorId: 1
|
|
64
|
+
}]
|
|
65
|
+
|
|
66
|
+
{
|
|
67
|
+
const res = await app.inject({
|
|
68
|
+
method: 'POST',
|
|
69
|
+
url: '/graphql',
|
|
70
|
+
body: {
|
|
71
|
+
query: `
|
|
72
|
+
mutation batch($inputs : [Test1AuthorInput]!) {
|
|
73
|
+
insertTest1Authors(inputs: $inputs) {
|
|
74
|
+
id
|
|
75
|
+
name
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`,
|
|
79
|
+
variables: {
|
|
80
|
+
inputs: authors
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
equal(res.statusCode, 200, 'authors status code')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
{
|
|
88
|
+
const res = await app.inject({
|
|
89
|
+
method: 'POST',
|
|
90
|
+
url: '/graphql',
|
|
91
|
+
body: {
|
|
92
|
+
query: `
|
|
93
|
+
mutation batch($inputs : [Test2BookInput]!) {
|
|
94
|
+
insertTest2Books(inputs: $inputs) {
|
|
95
|
+
id
|
|
96
|
+
title
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
`,
|
|
100
|
+
variables: {
|
|
101
|
+
inputs: books
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
equal(res.statusCode, 200, 'books status code')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
{
|
|
109
|
+
const res = await app.inject({
|
|
110
|
+
method: 'POST',
|
|
111
|
+
url: '/graphql',
|
|
112
|
+
body: {
|
|
113
|
+
query: `
|
|
114
|
+
query {
|
|
115
|
+
test2Books {
|
|
116
|
+
id
|
|
117
|
+
author {
|
|
118
|
+
id
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
`
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
equal(res.statusCode, 200, 'query books')
|
|
126
|
+
same(res.json(), {
|
|
127
|
+
data: {
|
|
128
|
+
test2Books: [{
|
|
129
|
+
id: 1,
|
|
130
|
+
author: {
|
|
131
|
+
id: 1
|
|
132
|
+
}
|
|
133
|
+
}]
|
|
134
|
+
}
|
|
135
|
+
}, 'query book response')
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
{
|
|
139
|
+
const res = await app.inject({
|
|
140
|
+
method: 'POST',
|
|
141
|
+
url: '/graphql',
|
|
142
|
+
body: {
|
|
143
|
+
query: `
|
|
144
|
+
query {
|
|
145
|
+
test1Authors {
|
|
146
|
+
id
|
|
147
|
+
test2Books {
|
|
148
|
+
id
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
`
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
equal(res.statusCode, 200, 'query authors')
|
|
156
|
+
same(res.json(), {
|
|
157
|
+
data: {
|
|
158
|
+
test1Authors: [{
|
|
159
|
+
id: 1,
|
|
160
|
+
test2Books: [{
|
|
161
|
+
id: 1
|
|
162
|
+
}]
|
|
163
|
+
}]
|
|
164
|
+
}
|
|
165
|
+
}, 'query authors response')
|
|
166
|
+
}
|
|
167
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
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, isPg } = require('./helper')
|
|
8
|
+
|
|
9
|
+
test('should correctly get the special characters', { skip: isSQLite }, async ({ pass, teardown, same }) => {
|
|
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 (isPg) {
|
|
19
|
+
await db.query(sql`
|
|
20
|
+
CREATE TYPE custom_enum AS ENUM ('application/pdf', 'image/jpeg', 'image/png');
|
|
21
|
+
CREATE TABLE enum_tests (
|
|
22
|
+
id INTEGER NOT NULL,
|
|
23
|
+
test_enum custom_enum,
|
|
24
|
+
PRIMARY KEY (id)
|
|
25
|
+
);`)
|
|
26
|
+
} else {
|
|
27
|
+
await db.query(sql`
|
|
28
|
+
CREATE TABLE enum_tests (
|
|
29
|
+
id INTEGER NOT NULL,
|
|
30
|
+
test_enum ENUM ('application/pdf', 'image/jpeg', 'image/png'),
|
|
31
|
+
PRIMARY KEY (id)
|
|
32
|
+
);`)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
app.register(sqlGraphQL)
|
|
38
|
+
teardown(app.close.bind(app))
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await app.ready()
|
|
42
|
+
same(true, true, 'Special characters are properly configured')
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.log(err)
|
|
45
|
+
same(true, false, 'Previous call should never fail')
|
|
46
|
+
}
|
|
47
|
+
})
|