Repository: newrelic/newrelic-node-apollo-server-plugin
Pull request #24 (2020-09-16 21:10:10)

Returns a noop plugin (empty object) when the agent is disabled.
Closes: https://github.com/newrelic/newrelic-node-apollo-server-plugin/issues/18

An empty object seems to work fine. Null/undefined does not.

This is currently assuming a disabled agent will have a no-op API that will not have a .shim and eventually might likely return an empty instrumentation API. We may tweak that to return a no-op of its own, so we'll have to modify as the design evolves.


Patch:
diff --git a/lib/create-plugin.js b/lib/create-plugin.js
index 7a9afb6..021d910 100644
--- a/lib/create-plugin.js
+++ b/lib/create-plugin.js
@@ -8,6 +8,10 @@ const ErrorHelper = require('./error-helper.js')
 const errorHelper = new ErrorHelper
 
 function createPlugin(instrumentationApi) {
+  if (!instrumentationApi) {
+    return {}
+  }
+
   return {
     requestDidStart() {
       const longestResolvedPath = {
diff --git a/package.json b/package.json
index 9697c2e..095d7bc 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,9 @@
   "description": "Apollo Server plugin that adds New Relic Node.js agent instrumentation.",
   "main": "/src/index.js",
   "scripts": {
+    "integration": "rm -f newrelic_agent.log && time tap --test-regex='(\\/|^tests\\/integration\\/.*\\.test\\.js)$' --no-coverage",
     "lint": "eslint *.js lib tests",
-    "test": "npm run unit",
+    "test": "npm run unit && npm run integration && npm run versioned",
     "unit": "tap --test-regex='(\\/|^tests\\/unit\\/.*\\.test\\.js)$' --no-coverage",
     "versioned": "versioned-tests --minor -i 2 'tests/versioned/*'"
   },
@@ -34,5 +35,6 @@
   },
   "peerDependencies": {
     "newrelic": ">=6.13.0"
-  }
+  },
+  "dependencies": {}
 }


Test patch:
diff --git a/tests/integration/bootstrap.test.js b/tests/integration/bootstrap.test.js
new file mode 100644
index 0000000..0a90450
--- /dev/null
+++ b/tests/integration/bootstrap.test.js
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 New Relic Corporation. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict'
+
+const tap = require('tap')
+
+const INDEX_PATH = '../..'
+
+tap.test('Should export plugin when loaded', (t) => {
+  const plugin = require(INDEX_PATH)
+  t.ok(plugin)
+
+  resetModuleCache(() => {
+    t.end()
+  })
+})
+
+tap.test('should export noop plugin when agent disabled', (t) => {
+  // w/o a config file the agent will be disabled by default
+  const plugin = require('../..')
+  t.ok(plugin)
+
+  t.notOk(plugin.requestDidStart)
+
+  resetModuleCache(() => {
+    t.end()
+  })
+})
+
+tap.test('should export full plugin when agent enabled', (t) => {
+  temporarySetEnv(t, 'NEW_RELIC_NO_CONFIG_FILE', true)
+  temporarySetEnv(t, 'NEW_RELIC_ENABLED', true)
+  temporarySetEnv(t, 'NEW_RELIC_APP_NAME', 'P L U G I N')
+
+  const plugin = require('../..')
+  t.ok(plugin)
+  t.ok(plugin.requestDidStart)
+
+  resetModuleCache(() => {
+    t.end()
+  })
+})
+
+function temporarySetEnv(t, key, value) {
+  const existing = process.env[key]
+  process.env[key] = value
+
+  t.tearDown(() => {
+    if (existing === undefined) {
+      delete process.env[key]
+      return
+    }
+
+    process.env[key] = existing
+  })
+}
+
+function resetModuleCache(callback) {
+  const indexPath = require.resolve(INDEX_PATH)
+  const newrelicPath = require.resolve('newrelic')
+
+  delete require.cache[indexPath]
+  delete require.cache[newrelicPath]
+  delete require.cache.__NR_cache
+
+  callback()
+}
diff --git a/tests/unit/bootstrap.test.js b/tests/unit/bootstrap.test.js
deleted file mode 100644
index 9126821..0000000
--- a/tests/unit/bootstrap.test.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2020 New Relic Corporation. All rights reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-'use strict'
-
-const tap = require('tap')
-
-// TODO: this is just to exercise initial module setup.
-// delete/replace as real functionality is added.
-tap.test('Should export plugin when loaded', (t) => {
-  const plugin = require('../../')
-  t.ok(plugin)
-
-  t.end()
-})
diff --git a/tests/unit/error-helper.js b/tests/unit/error-helper.test.js
similarity index 100%
rename from tests/unit/error-helper.js
rename to tests/unit/error-helper.test.js
diff --git a/tests/versioned/apollo-server/agent-disabled.test.js b/tests/versioned/apollo-server/agent-disabled.test.js
new file mode 100644
index 0000000..88b6820
--- /dev/null
+++ b/tests/versioned/apollo-server/agent-disabled.test.js
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2020 New Relic Corporation. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict'
+
+const tap = require('tap')
+const { ApolloServer, gql } = require('apollo-server')
+
+const { getTypeDefs, resolvers } = require('../data-definitions')
+const { executeQuery } = require('../test-client')
+
+tap.test('Agent disabled', (t) => {
+  t.autoend()
+
+  let server = null
+  let serverUrl = null
+
+  t.beforeEach((done) => {
+    const createPlugin = require('../../../lib/create-plugin')
+
+    // when the agent is disabled, the agent API will be a no-op API
+    const plugin = createPlugin()
+
+    server = new ApolloServer({
+      typeDefs: getTypeDefs(gql),
+      resolvers,
+      plugins: [plugin]
+    })
+
+    server.listen().then(({ url }) => {
+      serverUrl = url
+
+      done()
+    })
+  })
+
+  t.afterEach((done) => {
+    server.stop()
+
+    server = null
+    serverUrl = null
+
+    done()
+  })
+
+
+  t.test('should not break existing functionality', (t) => {
+    const query = `query {
+      hello
+    }`
+
+    executeQuery(serverUrl, query, (err, result) => {
+      t.error(err)
+
+      t.ok(result)
+      t.deepEqual(result.data, { hello: 'hello world' })
+
+      if (result.errors) {
+        result.errors.forEach((error) => {
+          t.error(error)
+        })
+      }
+
+      t.end()
+    })
+  })
+})