Skip to content

Commit

Permalink
Merge pull request #859 from drakhart/fix-batched-queries-context
Browse files Browse the repository at this point in the history
Fix batched queries context cloning
  • Loading branch information
simoneb committed Sep 1, 2022
2 parents 7ea5e4a + 6acc199 commit 1e641bb
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 1 deletion.
7 changes: 6 additions & 1 deletion lib/routes.js
Expand Up @@ -358,7 +358,12 @@ module.exports = async function (app, opts) {

return Promise.all(request.body.map(async (r, operationId) => {
// Create individual reqs for multiple operations, otherwise reference the original req
const operationReq = operationsCount > 1 ? Object.create(request) : request
const operationReq = operationsCount > 1
? {
...request,
[kRequestContext]: Object.create(request[kRequestContext])
}
: request

Object.assign(operationReq[kRequestContext], { operationId })

Expand Down
71 changes: 71 additions & 0 deletions test/batched.js
Expand Up @@ -388,3 +388,74 @@ test('POST batched query has an individual context for each operation', async (t
sinon.assert.calledWith(contextSpy, 0, 2, sinon.match(/TestQuery/))
sinon.assert.calledWith(contextSpy, 1, 2, sinon.match(/DoubleQuery/))
})

test('POST batched query respects custom class-based context', async (t) => {
const app = Fastify()

const schema = `
type Query {
test: String
}
`

class CustomContext {
constructor () {
this.test = 'custom'
}

method () {
return this.test
}
}

const resolvers = {
test: async (args, ctx) => {
t.type(ctx, 'object')
t.type(ctx.reply, 'object')
t.type(ctx.app, 'object')
t.type(ctx.method, 'function')
t.equal(ctx.test, 'custom')
t.equal(ctx.method(), 'custom')
t.equal(ctx.constructor, CustomContext)
return ctx.method()
}
}

app.register(GQL, {
schema,
resolvers,
context: (request, reply) => {
t.type(request, 'object')
t.type(reply, 'object')
return new CustomContext()
},
allowBatchedQueries: true
})

const post = await app.inject({
method: 'POST',
url: '/graphql',
body: [
{
operationName: 'TestQuery',
query: 'query TestQuery { test }'
},
{
operationName: 'DoubleQuery',
query: 'query DoubleQuery { test }'
}
]
})

t.same(JSON.parse(post.body), [
{
data: {
test: 'custom'
}
}, {
data: {
test: 'custom'
}
}
])
})
78 changes: 78 additions & 0 deletions test/hooks-with-batching.js
@@ -0,0 +1,78 @@
'use strict'

const { test } = require('tap')
const Fastify = require('fastify')
const sinon = require('sinon')
const GQL = require('..')

test('batched query has an individual context for each operation through all the lifecycle hooks', async (t) => {
const app = Fastify()

const preParsingSpy = sinon.spy()
const preValidationSpy = sinon.spy()
const preExecutionSpy = sinon.spy()
const onResolutionSpy = sinon.spy()

const schema = `
type Query {
test: String
}
`

const resolvers = {
test: () => 'test'
}

await app.register(GQL, {
schema,
resolvers,
allowBatchedQueries: true
})

app.graphql.addHook('preParsing', (_, __, ctx) => {
preParsingSpy(ctx.operationId, ctx.operationsCount, ctx.__currentQuery)
})

app.graphql.addHook('preValidation', (_, __, ctx) => {
preValidationSpy(ctx.operationId, ctx.operationsCount, ctx.__currentQuery)
})

app.graphql.addHook('preExecution', (_, __, ctx) => {
preExecutionSpy(ctx.operationId, ctx.operationsCount, ctx.__currentQuery)
})

app.graphql.addHook('onResolution', (_, ctx) => {
onResolutionSpy(ctx.operationId, ctx.operationsCount, ctx.__currentQuery)
})

await app.inject({
method: 'POST',
url: '/graphql',
body: [
{
operationName: 'TestQuery',
query: 'query TestQuery { test }'
},
{
operationName: 'DoubleQuery',
query: 'query DoubleQuery { test }'
}
]
})

sinon.assert.calledTwice(preParsingSpy)
sinon.assert.calledWith(preParsingSpy, 0, 2, sinon.match(/TestQuery/))
sinon.assert.calledWith(preParsingSpy, 1, 2, sinon.match(/DoubleQuery/))

sinon.assert.calledTwice(preValidationSpy)
sinon.assert.calledWith(preValidationSpy, 0, 2, sinon.match(/TestQuery/))
sinon.assert.calledWith(preValidationSpy, 1, 2, sinon.match(/DoubleQuery/))

sinon.assert.calledTwice(preExecutionSpy)
sinon.assert.calledWith(preExecutionSpy, 0, 2, sinon.match(/TestQuery/))
sinon.assert.calledWith(preExecutionSpy, 1, 2, sinon.match(/DoubleQuery/))

sinon.assert.calledTwice(onResolutionSpy)
sinon.assert.calledWith(onResolutionSpy, 0, 2, sinon.match(/TestQuery/))
sinon.assert.calledWith(onResolutionSpy, 1, 2, sinon.match(/DoubleQuery/))
})

0 comments on commit 1e641bb

Please sign in to comment.