Repository: mcollina/fastify-gql
Pull request #240 (2020-08-07 08:12:49)

Fix custom context with loaders


Patch:
diff --git a/index.js b/index.js
index ce8aa2a..af29577 100644
--- a/index.js
+++ b/index.js
@@ -33,6 +33,7 @@ const { ErrorWithProps, defaultErrorFormatter } = require('./lib/errors')
 const persistedQueryDefaults = require('./lib/persistedQueryDefaults')
 
 const kLoaders = Symbol('fastify-gql.loaders')
+const kFactory = Symbol('fastify-gql.loadersFactory')
 
 function buildCache (opts) {
   if (Object.prototype.hasOwnProperty.call(opts, 'cache')) {
@@ -216,7 +217,12 @@ const plugin = fp(async function (app, opts) {
   app.decorateReply(graphqlCtx, null)
 
   app.decorateReply('graphql', function (source, context, variables, operationName) {
-    return app.graphql(source, Object.assign({ reply: this }, context), variables, operationName)
+    context = Object.assign({ reply: this, app }, context)
+    if (app[kFactory]) {
+      this[kLoaders] = factory.create(context)
+    }
+
+    return app.graphql(source, context, variables, operationName)
   })
 
   app.decorate('graphql', fastifyGraphQl)
@@ -301,9 +307,7 @@ const plugin = fp(async function (app, opts) {
     if (!factory) {
       factory = new Factory()
       app.decorateReply(kLoaders)
-      app.addHook('onRequest', async function (req, reply) {
-        reply[kLoaders] = factory.create({ req, reply, app })
-      })
+      app.decorate(kFactory, factory)
     }
 
     function defineLoader (name) {
diff --git a/lib/queryDepth.js b/lib/queryDepth.js
index 279a5c7..4dd7cc8 100644
--- a/lib/queryDepth.js
+++ b/lib/queryDepth.js
@@ -24,7 +24,7 @@ function queryDepth (definitions, queryDepthLimit) {
     for (const query of Object.keys(queryDepth)) {
       const totalDepth = queryDepth[query]
       if (totalDepth > queryDepthLimit) {
-        const queryDepthError = new Error(`${query} query exceeds the query depth limit of ${queryDepthLimit}`)
+        const queryDepthError = new Error(`${query} query depth (${totalDepth}) exceeds the query depth limit of ${queryDepthLimit}`)
         errors.push(queryDepthError)
       }
     }
@@ -33,13 +33,10 @@ function queryDepth (definitions, queryDepthLimit) {
   return errors
 }
 function determineDepth (node, current = 0) {
-  let result = current
   if (node.selectionSet) {
-    for (const selection of node.selectionSet.selections) {
-      result = determineDepth(selection, current++)
-    }
+    return Math.max(...node.selectionSet.selections.map((selection) => determineDepth(selection, current + 1)))
   }
-  return result
+  return current
 }
 function getQueriesAndMutations (definitions) {
   return definitions.reduce((map, definition) => {
diff --git a/package.json b/package.json
index 55c4ce7..59bc2a4 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,7 @@
     "@types/ws": "^7.2.6",
     "@typescript-eslint/eslint-plugin": "^3.6.1",
     "@typescript-eslint/parser": "^3.6.1",
-    "autocannon": "^5.0.0",
+    "autocannon": "^6.0.0",
     "fastify": "^3.0.2",
     "graphql-tools": "^6.0.14",
     "pre-commit": "^1.2.2",


Test patch:
diff --git a/test/loaders.js b/test/loaders.js
index 42c0caf..0d5c8e4 100644
--- a/test/loaders.js
+++ b/test/loaders.js
@@ -467,3 +467,79 @@ test('throw when persistedQueries is empty but onlyPersisted is true', async t =
 
   t.rejects(app.ready(), 'onlyPersisted is true but there are no persistedQueries')
 })
+
+test('loaders support custom context', async (t) => {
+  const app = Fastify()
+
+  const loaders = {
+    Dog: {
+      async owner (queries, { reply, test }) {
+        t.is(test, 'custom')
+        // note that the second entry for max is cached
+        t.deepEqual(queries, [{
+          obj: {
+            name: 'Max'
+          },
+          params: {}
+        }, {
+          obj: {
+            name: 'Charlie'
+          },
+          params: {}
+        }, {
+          obj: {
+            name: 'Buddy'
+          },
+          params: {}
+        }])
+        return queries.map(({ obj }) => owners[obj.name])
+      }
+    }
+  }
+
+  app.register(GQL, {
+    schema,
+    resolvers,
+    loaders,
+    context: (request, reply) => {
+      return {
+        test: 'custom'
+      }
+    }
+  })
+
+  const res = await app.inject({
+    method: 'POST',
+    url: '/graphql',
+    body: {
+      query
+    }
+  })
+
+  t.equal(res.statusCode, 200)
+  t.deepEqual(JSON.parse(res.body), {
+    data: {
+      dogs: [{
+        name: 'Max',
+        owner: {
+          name: 'Jennifer'
+        }
+      }, {
+        name: 'Charlie',
+        owner: {
+          name: 'Sarah'
+        }
+      }, {
+        name: 'Buddy',
+        owner: {
+          name: 'Tracy'
+        }
+      }, {
+        name: 'Max',
+        owner: {
+          name: 'Jennifer'
+        }
+      }]
+    }
+  })
+})
diff --git a/test/query-depth.js b/test/query-depth.js
index 5337209..151bd08 100644
--- a/test/query-depth.js
+++ b/test/query-depth.js
@@ -144,7 +144,7 @@ test('queryDepth - test total depth is within queryDepth parameter', async (t) =
   app.register(GQL, {
     schema,
     resolvers,
-    queryDepth: 5
+    queryDepth: 6
   })
 
   // needed so that graphql is defined
@@ -162,14 +162,14 @@ test('queryDepth - test total depth is over queryDepth parameter', async (t) =>
   app.register(GQL, {
     schema,
     resolvers,
-    queryDepth: 3
+    queryDepth: 5
   })
 
   // needed so that graphql is defined
   await app.ready()
 
   const err = new BadRequest()
-  const queryDepthError = new Error('unnamedQuery query exceeds the query depth limit of 3')
+  const queryDepthError = new Error('unnamedQuery query depth (6) exceeds the query depth limit of 5')
   err.errors = [queryDepthError]
 
   try {
@@ -303,6 +303,7 @@ test('queryDepth - ensure query depth is correctly calculated', async (t) => {
           {
             id: 1,
             name: 'Strawberry',
+            description: 'Lorem ipsum',
             seasons: [
               { name: 'Spring' },
               { name: 'Summer' }
@@ -356,6 +357,7 @@ test('queryDepth - ensure query depth is correctly calculated', async (t) => {
     flavors {
       id
       name
+      description
       nutrition {
         calories,
         description {
@@ -394,6 +396,7 @@ test('queryDepth - ensure query depth is correctly calculated', async (t) => {
         {
           id: '1',
           name: 'Strawberry',
+          description: 'Lorem ipsum',
           nutrition: {
             calories: 123,
             description: {