@platformatic/sql-graphql 0.8.0 → 0.9.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/index.js CHANGED
@@ -16,6 +16,7 @@ async function mapperToGraphql (app, opts) {
16
16
  const loaders = {}
17
17
  const federationReplacements = []
18
18
  const relations = []
19
+ const ignore = opts.ignore || {}
19
20
 
20
21
  const graphOpts = {
21
22
  queryTopFields,
@@ -35,8 +36,11 @@ async function mapperToGraphql (app, opts) {
35
36
  }
36
37
  } else {
37
38
  for (const entity of Object.values(mapper.entities)) {
39
+ if (ignore[entity.pluralName] === true) {
40
+ continue
41
+ }
38
42
  relations.push(...entity.relations)
39
- const meta = constructGraph(app, entity, graphOpts)
43
+ const meta = constructGraph(app, entity, graphOpts, ignore[entity.pluralName] || {})
40
44
  metaMap.set(entity, meta)
41
45
  }
42
46
 
@@ -89,10 +93,23 @@ async function mapperToGraphql (app, opts) {
89
93
 
90
94
  let subscription
91
95
  if (app.platformatic.mq) {
92
- const entitiesList = Object.keys(app.platformatic.entities)
93
- const ignoreList = opts.subscriptionIgnore || []
96
+ // Create a copy because we will alter it
97
+ const ignoreList = Array.from(opts.subscriptionIgnore || [])
98
+
99
+ const entitiesList = []
100
+
101
+ for (const entity of Object.values(app.platformatic.entities)) {
102
+ if (ignoreList.includes(entity.singularName)) {
103
+ continue
104
+ }
105
+ // We currently do not support subscriptions for join tables
106
+ if (entity.primaryKeys.size > 1) {
107
+ continue
108
+ }
109
+ entitiesList.push(entity)
110
+ }
94
111
 
95
- if (entitiesList.length - ignoreList.length > 0) {
112
+ if (entitiesList.length > 0) {
96
113
  opts.subscription = {
97
114
  emitter: app.platformatic.mq
98
115
  }
@@ -15,7 +15,12 @@ const ascDesc = new graphql.GraphQLEnumType({
15
15
  }
16
16
  })
17
17
 
18
- function constructGraph (app, entity, opts) {
18
+ const limitType = new graphql.GraphQLScalarType({
19
+ name: 'LimitInt',
20
+ description: 'Limit will be applied by default if not passed. If the provided value exceeds the maximum allowed value a validation error will be thrown'
21
+ })
22
+
23
+ function constructGraph (app, entity, opts, ignore) {
19
24
  const primaryKeys = Array.from(entity.primaryKeys).map((key) => camelcase(key))
20
25
  const relationalFields = entity.relations
21
26
  .map((relation) => relation.column_name)
@@ -35,6 +40,10 @@ function constructGraph (app, entity, opts) {
35
40
 
36
41
  for (const key of Object.keys(entity.fields)) {
37
42
  const field = entity.fields[key]
43
+ if (ignore[field.camelcase]) {
44
+ continue
45
+ }
46
+
38
47
  const meta = { field }
39
48
 
40
49
  // sqlite doesn't support enums
@@ -134,7 +143,7 @@ function constructGraph (app, entity, opts) {
134
143
  queryTopFields[plural] = {
135
144
  type: new graphql.GraphQLList(type),
136
145
  args: {
137
- limit: { type: graphql.GraphQLInt },
146
+ limit: { type: limitType },
138
147
  offset: { type: graphql.GraphQLInt },
139
148
  orderBy: {
140
149
  type: new graphql.GraphQLList(new graphql.GraphQLInputObjectType({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/sql-graphql",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Map SQL dbs to GraphQL",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -14,13 +14,13 @@
14
14
  },
15
15
  "homepage": "https://github.com/platformatic/platformatic#readme",
16
16
  "devDependencies": {
17
- "@platformatic/sql-events": "0.8.0",
18
- "@platformatic/sql-mapper": "0.8.0",
17
+ "@platformatic/sql-events": "0.9.1",
18
+ "@platformatic/sql-mapper": "0.9.1",
19
19
  "fastify": "^4.6.0",
20
20
  "snazzy": "^9.0.0",
21
21
  "standard": "^17.0.0",
22
22
  "tap": "^16.0.0",
23
- "tsd": "^0.24.0",
23
+ "tsd": "^0.25.0",
24
24
  "ws": "^8.9.0"
25
25
  },
26
26
  "dependencies": {
@@ -4,6 +4,7 @@ const { clear, connInfo, isSQLite, isMysql, isPg } = require('./helper')
4
4
  const { test } = require('tap')
5
5
  const fastify = require('fastify')
6
6
  const sqlMapper = require('@platformatic/sql-mapper')
7
+ const sqlEvents = require('@platformatic/sql-events')
7
8
  const sqlGraphQL = require('..')
8
9
 
9
10
  test('composite primary keys', async ({ equal, same, teardown, rejects }) => {
@@ -76,6 +77,7 @@ test('composite primary keys', async ({ equal, same, teardown, rejects }) => {
76
77
  ...connInfo,
77
78
  onDatabaseLoad
78
79
  })
80
+ app.register(sqlEvents) // needed as if it's present it might throw
79
81
  app.register(sqlGraphQL)
80
82
  teardown(app.close.bind(app))
81
83
 
@@ -329,3 +331,144 @@ test('composite primary keys', async ({ equal, same, teardown, rejects }) => {
329
331
  }, 'editor response')
330
332
  }
331
333
  })
334
+
335
+ test('composite primary keys with no foreign keys', async ({ equal, same, teardown, rejects }) => {
336
+ /* https://github.com/platformatic/platformatic/issues/299 */
337
+ async function onDatabaseLoad (db, sql) {
338
+ await clear(db, sql)
339
+
340
+ await db.query(sql`CREATE TABLE editors (
341
+ page_id INTEGER NOT NULL,
342
+ user_id INTEGER NOT NULL,
343
+ role VARCHAR(255) NOT NULL,
344
+ PRIMARY KEY (page_id, user_id)
345
+ );`)
346
+ }
347
+
348
+ const app = fastify()
349
+ app.register(sqlMapper, {
350
+ ...connInfo,
351
+ onDatabaseLoad
352
+ })
353
+ app.register(sqlEvents) // needed as if it's present it will throw
354
+ app.register(sqlGraphQL)
355
+ teardown(app.close.bind(app))
356
+
357
+ await app.ready()
358
+
359
+ {
360
+ const res = await app.inject({
361
+ method: 'POST',
362
+ url: '/graphql',
363
+ body: {
364
+ query: `
365
+ mutation {
366
+ saveEditor(input: { userId: "1", pageId: "1", role: "admin" }) {
367
+ userId
368
+ pageId
369
+ role
370
+ }
371
+ }
372
+ `
373
+ }
374
+ })
375
+ equal(res.statusCode, 200, 'saveEditor status code')
376
+ same(res.json(), {
377
+ data: {
378
+ saveEditor: {
379
+ userId: '1',
380
+ pageId: '1',
381
+ role: 'admin'
382
+ }
383
+ }
384
+ }, 'saveEditor response')
385
+ }
386
+
387
+ {
388
+ const res = await app.inject({
389
+ method: 'POST',
390
+ url: '/graphql',
391
+ body: {
392
+ query: `
393
+ mutation {
394
+ saveEditor(input: { userId: "2", pageId: "1", role: "author" }) {
395
+ userId
396
+ pageId
397
+ role
398
+ }
399
+ }
400
+ `
401
+ }
402
+ })
403
+ equal(res.statusCode, 200, 'saveEditor status code')
404
+ same(res.json(), {
405
+ data: {
406
+ saveEditor: {
407
+ userId: '2',
408
+ pageId: '1',
409
+ role: 'author'
410
+ }
411
+ }
412
+ }, 'saveEditor response')
413
+ }
414
+
415
+ {
416
+ const res = await app.inject({
417
+ method: 'POST',
418
+ url: '/graphql',
419
+ body: {
420
+ query: `
421
+ mutation {
422
+ saveEditor(input: { userId: "1", pageId: "1", role: "captain" }) {
423
+ userId
424
+ pageId
425
+ role
426
+ }
427
+ }
428
+ `
429
+ }
430
+ })
431
+ equal(res.statusCode, 200, 'saveEditor status code')
432
+ same(res.json(), {
433
+ data: {
434
+ saveEditor: {
435
+ userId: 1,
436
+ pageId: 1,
437
+ role: 'captain'
438
+ }
439
+ }
440
+ }, 'saveEditor response')
441
+ }
442
+
443
+ {
444
+ const res = await app.inject({
445
+ method: 'post',
446
+ url: '/graphql',
447
+ body: {
448
+ query: `
449
+ query {
450
+ editors(orderBy: { field: role, direction: DESC }) {
451
+ userId
452
+ pageId
453
+ role
454
+ }
455
+ }
456
+ `
457
+ }
458
+ })
459
+ equal(res.statusCode, 200, 'editors status code')
460
+ same(res.json(), {
461
+ data: {
462
+ editors: [{
463
+ userId: '1',
464
+ pageId: '1',
465
+ role: 'captain'
466
+ }, {
467
+ userId: '2',
468
+ pageId: '1',
469
+ role: 'author'
470
+ }]
471
+ }
472
+ }, 'editor response')
473
+ }
474
+ })
@@ -115,3 +115,93 @@ test('ignore a column', async ({ pass, teardown, equal }) => {
115
115
  equal(Category.fields.find((f) => f.name === 'name'), undefined, 'name column is ignored')
116
116
  }
117
117
  })
118
+
119
+ test('ignore a table via sql-graphql option', async ({ pass, teardown, equal }) => {
120
+ const app = fastify()
121
+ app.register(sqlMapper, {
122
+ ...connInfo,
123
+ async onDatabaseLoad (db, sql) {
124
+ pass('onDatabaseLoad called')
125
+
126
+ await clear(db, sql)
127
+ await createBasicPages(db, sql)
128
+ }
129
+ })
130
+ app.register(sqlGraphQL, {
131
+ ignore: {
132
+ categories: true
133
+ }
134
+ })
135
+ teardown(app.close.bind(app))
136
+
137
+ await app.ready()
138
+
139
+ {
140
+ const res = await app.inject({
141
+ method: 'POST',
142
+ url: '/graphql',
143
+ body: {
144
+ query: `
145
+ query {
146
+ __schema {
147
+ types {
148
+ name
149
+ }
150
+ }
151
+ }
152
+ `
153
+ }
154
+ })
155
+ equal(res.statusCode, 200, 'introspection query status code')
156
+ const data = res.json().data
157
+ equal(data.__schema.types.find((t) => t.name === 'Category'), undefined, 'Category type is ignored')
158
+ }
159
+ })
160
+
161
+ test('ignore a column via sql-graphql option', async ({ pass, teardown, equal }) => {
162
+ const app = fastify()
163
+ app.register(sqlMapper, {
164
+ ...connInfo,
165
+ async onDatabaseLoad (db, sql) {
166
+ pass('onDatabaseLoad called')
167
+
168
+ await clear(db, sql)
169
+ await createBasicPages(db, sql)
170
+ }
171
+ })
172
+ app.register(sqlGraphQL, {
173
+ ignore: {
174
+ categories: {
175
+ name: true
176
+ }
177
+ }
178
+ })
179
+ teardown(app.close.bind(app))
180
+
181
+ await app.ready()
182
+
183
+ {
184
+ const res = await app.inject({
185
+ method: 'POST',
186
+ url: '/graphql',
187
+ body: {
188
+ query: `
189
+ query {
190
+ __schema {
191
+ types {
192
+ name
193
+ fields {
194
+ name
195
+ }
196
+ }
197
+ }
198
+ }
199
+ `
200
+ }
201
+ })
202
+ equal(res.statusCode, 200, 'introspection query status code')
203
+ const data = res.json().data
204
+ const Category = data.__schema.types.find((t) => t.name === 'Category')
205
+ equal(Category.fields.find((f) => f.name === 'name'), undefined, 'name column is ignored')
206
+ }
207
+ })