Skip to content

Commit a7957bb

Browse files
committed
feat(apollo-server-express): add support for v2.x (#641)
1 parent dd89b92 commit a7957bb

File tree

8 files changed

+269
-5
lines changed

8 files changed

+269
-5
lines changed

.tav.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,25 @@ express-graphql-0.6.12_13:
116116
versions: '>=0.6.12'
117117
commands: node test/instrumentation/modules/express-graphql.js
118118

119+
apollo-server-express-2_12:
120+
name: apollo-server-express
121+
peerDependencies: graphql@^0.12.0
122+
versions: '>=2.0.2'
123+
node: '>=6'
124+
commands: node test/instrumentation/modules/apollo-server-express.js
125+
apollo-server-express-2_13:
126+
name: apollo-server-express
127+
peerDependencies: graphql@^0.13.0
128+
versions: '>=2.0.2'
129+
node: '>=6'
130+
commands: node test/instrumentation/modules/apollo-server-express.js
131+
apollo-server-express-2_14:
132+
name: apollo-server-express
133+
peerDependencies: graphql@^14.0.0
134+
versions: '>=2.0.2'
135+
node: '>=6'
136+
commands: node test/instrumentation/modules/apollo-server-express.js
137+
119138
express-queue:
120139
versions: '>=0.0.11'
121140
commands: node test/instrumentation/modules/express-queue.js

.travis.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
-
9595
node_js: '10'
9696
if: type IN (cron, pull_request) AND NOT branch =~ ^greenkeeper/.*
97-
env: TAV=knex,ws,graphql,express-graphql,elasticsearch,hapi,express,express-queue
97+
env: TAV=knex,ws,graphql,express-graphql,elasticsearch,hapi,express,express-queue,apollo-server-express
9898
script: tav --quiet
9999

100100
# Node.js 9
@@ -116,7 +116,7 @@ jobs:
116116
-
117117
node_js: '9'
118118
if: type IN (cron, pull_request) AND NOT branch =~ ^greenkeeper/.*
119-
env: TAV=knex,ws,graphql,express-graphql,elasticsearch,hapi,express,express-queue
119+
env: TAV=knex,ws,graphql,express-graphql,elasticsearch,hapi,express,express-queue,apollo-server-express
120120
script: tav --quiet
121121

122122
# Node.js 8
@@ -138,7 +138,7 @@ jobs:
138138
-
139139
node_js: '8'
140140
if: type IN (cron, pull_request) AND NOT branch =~ ^greenkeeper/.*
141-
env: TAV=knex,ws,graphql,express-graphql,elasticsearch,hapi,express,express-queue
141+
env: TAV=knex,ws,graphql,express-graphql,elasticsearch,hapi,express,express-queue,apollo-server-express
142142
script: tav --quiet
143143

144144
# Node.js 6
@@ -160,7 +160,7 @@ jobs:
160160
-
161161
node_js: '6'
162162
if: type IN (cron, pull_request) AND NOT branch =~ ^greenkeeper/.*
163-
env: TAV=knex,ws,graphql,express-graphql,elasticsearch,hapi,express,express-queue
163+
env: TAV=knex,ws,graphql,express-graphql,elasticsearch,hapi,express,express-queue,apollo-server-express
164164
script: tav --quiet
165165

166166
# Node.js 4

lib/instrumentation/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var Transaction = require('./transaction')
1313
var shimmer = require('./shimmer')
1414

1515
var MODULES = [
16+
'apollo-server-core',
1617
'bluebird',
1718
'cassandra-driver',
1819
'elasticsearch',
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict'
2+
3+
var semver = require('semver')
4+
5+
module.exports = function (apolloServerCore, agent, version, enabled) {
6+
if (!enabled) return apolloServerCore
7+
8+
if (!semver.satisfies(version, '^2.0.2') || typeof apolloServerCore.runHttpQuery !== 'function') {
9+
agent.logger.debug('apollo-server-core version %s not supported - aborting...', version)
10+
return apolloServerCore
11+
}
12+
13+
var runHttpQuery = apolloServerCore.runHttpQuery
14+
apolloServerCore.runHttpQuery = wrappedRunHttpQuery
15+
16+
return apolloServerCore
17+
18+
function wrappedRunHttpQuery () {
19+
var trans = agent._instrumentation.currentTransaction
20+
if (trans) trans._graphqlRoute = true
21+
return runHttpQuery.apply(this, arguments)
22+
}
23+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
"@commitlint/cli": "^7.0.0",
9696
"@commitlint/config-conventional": "^7.0.1",
9797
"@commitlint/travis-cli": "^7.0.0",
98+
"apollo-server-express": "^2.0.2",
9899
"bluebird": "^3.4.6",
99100
"cassandra-driver": "^3.5.0",
100101
"connect": "^3.6.3",

test/.jenkins_tav.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ TAV:
22
- generic-pool+mysql+mysql2+redis+koa-router+handlebars+mongodb-core
33
- ioredis+pg+cassandra-driver+tedious+restify
44
- mimic-response+got+bluebird
5-
- knex+ws+graphql+express-graphql+elasticsearch+hapi+express+express-queue
5+
- knex+ws+graphql+express-graphql+elasticsearch+hapi+express+express-queue+apollo-server-express

test/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ test('disableInstrumentations', function (t) {
376376
}
377377
if (semver.lt(process.version, '6.0.0')) {
378378
modules.delete('express-queue')
379+
modules.delete('apollo-server-core')
379380
}
380381

381382
function testSlice (t, name, selector) {
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
'use strict'
2+
3+
var agent = require('../../..').start({
4+
serviceName: 'test',
5+
secretToken: 'test',
6+
captureExceptions: false
7+
})
8+
9+
var semver = require('semver')
10+
if (semver.lt(process.version, '6.0.0')) process.exit()
11+
12+
var test = require('tape')
13+
14+
var http = require('http')
15+
var express = require('express')
16+
var querystring = require('querystring')
17+
18+
var ApolloServer = require('apollo-server-express').ApolloServer
19+
var gql = require('apollo-server-express').gql
20+
21+
test('POST /graphql', function (t) {
22+
resetAgent(done(t, 'hello'))
23+
24+
var typeDefs = gql`
25+
type Query {
26+
hello: String
27+
}
28+
`
29+
var resolvers = {
30+
Query: {
31+
hello () {
32+
t.ok(agent._instrumentation.currentTransaction, 'have active transaction')
33+
return 'Hello world!'
34+
}
35+
}
36+
}
37+
var query = '{"query":"{ hello }"}'
38+
39+
var app = express()
40+
var apollo = new ApolloServer({ typeDefs, resolvers })
41+
apollo.applyMiddleware({ app })
42+
var server = app.listen(function () {
43+
var port = server.address().port
44+
var opts = {
45+
method: 'POST',
46+
port: port,
47+
path: '/graphql',
48+
headers: { 'Content-Type': 'application/json' }
49+
}
50+
var req = http.request(opts, function (res) {
51+
var chunks = []
52+
res.on('data', chunks.push.bind(chunks))
53+
res.on('end', function () {
54+
server.close()
55+
var result = Buffer.concat(chunks).toString()
56+
t.equal(result, '{"data":{"hello":"Hello world!"}}\n')
57+
agent.flush()
58+
})
59+
})
60+
req.end(query)
61+
})
62+
})
63+
64+
test('GET /graphql', function (t) {
65+
resetAgent(done(t, 'hello'))
66+
67+
var typeDefs = gql`
68+
type Query {
69+
hello: String
70+
}
71+
`
72+
var resolvers = {
73+
Query: {
74+
hello () {
75+
t.ok(agent._instrumentation.currentTransaction, 'have active transaction')
76+
return 'Hello world!'
77+
}
78+
}
79+
}
80+
var query = querystring.stringify({ query: '{ hello }' })
81+
82+
var app = express()
83+
var apollo = new ApolloServer({ typeDefs, resolvers })
84+
apollo.applyMiddleware({ app })
85+
var server = app.listen(function () {
86+
var port = server.address().port
87+
var opts = {
88+
method: 'GET',
89+
port: port,
90+
path: '/graphql?' + query
91+
}
92+
var req = http.request(opts, function (res) {
93+
var chunks = []
94+
res.on('data', chunks.push.bind(chunks))
95+
res.on('end', function () {
96+
server.close()
97+
var result = Buffer.concat(chunks).toString()
98+
t.equal(result, '{"data":{"hello":"Hello world!"}}\n')
99+
agent.flush()
100+
})
101+
})
102+
req.end()
103+
})
104+
})
105+
106+
test('POST /graphql - named query', function (t) {
107+
resetAgent(done(t, 'HelloQuery hello'))
108+
109+
var typeDefs = gql`
110+
type Query {
111+
hello: String
112+
}
113+
`
114+
var resolvers = {
115+
Query: {
116+
hello () {
117+
t.ok(agent._instrumentation.currentTransaction, 'have active transaction')
118+
return 'Hello world!'
119+
}
120+
}
121+
}
122+
var query = '{"query":"query HelloQuery { hello }"}'
123+
124+
var app = express()
125+
var apollo = new ApolloServer({ typeDefs, resolvers })
126+
apollo.applyMiddleware({ app })
127+
var server = app.listen(function () {
128+
var port = server.address().port
129+
var opts = {
130+
method: 'POST',
131+
port: port,
132+
path: '/graphql',
133+
headers: { 'Content-Type': 'application/json' }
134+
}
135+
var req = http.request(opts, function (res) {
136+
var chunks = []
137+
res.on('data', chunks.push.bind(chunks))
138+
res.on('end', function () {
139+
server.close()
140+
var result = Buffer.concat(chunks).toString()
141+
t.equal(result, '{"data":{"hello":"Hello world!"}}\n')
142+
agent.flush()
143+
})
144+
})
145+
req.end(query)
146+
})
147+
})
148+
149+
test('POST /graphql - sort multiple queries', function (t) {
150+
resetAgent(done(t, 'hello, life'))
151+
152+
var typeDefs = gql`
153+
type Query {
154+
hello: String
155+
life: Int
156+
}
157+
`
158+
var resolvers = {
159+
Query: {
160+
hello () {
161+
t.ok(agent._instrumentation.currentTransaction, 'have active transaction')
162+
return 'Hello world!'
163+
},
164+
life () {
165+
t.ok(agent._instrumentation.currentTransaction, 'have active transaction')
166+
return 42
167+
}
168+
}
169+
}
170+
var query = '{"query":"{ life, hello }"}'
171+
172+
var app = express()
173+
var apollo = new ApolloServer({ typeDefs, resolvers })
174+
apollo.applyMiddleware({ app })
175+
var server = app.listen(function () {
176+
var port = server.address().port
177+
var opts = {
178+
method: 'POST',
179+
port: port,
180+
path: '/graphql',
181+
headers: { 'Content-Type': 'application/json' }
182+
}
183+
var req = http.request(opts, function (res) {
184+
var chunks = []
185+
res.on('data', chunks.push.bind(chunks))
186+
res.on('end', function () {
187+
server.close()
188+
var result = Buffer.concat(chunks).toString()
189+
t.equal(result, '{"data":{"life":42,"hello":"Hello world!"}}\n')
190+
agent.flush()
191+
})
192+
})
193+
req.end(query)
194+
})
195+
})
196+
197+
function done (t, query) {
198+
return function (endpoint, headers, data, cb) {
199+
t.equal(data.transactions.length, 1)
200+
201+
var trans = data.transactions[0]
202+
203+
t.equal(trans.name, query + ' (/graphql)')
204+
t.equal(trans.type, 'request')
205+
t.equal(trans.spans.length, 1)
206+
t.equal(trans.spans[0].name, 'GraphQL: ' + query)
207+
t.equal(trans.spans[0].type, 'db.graphql.execute')
208+
t.ok(trans.spans[0].start + trans.spans[0].duration < trans.duration)
209+
210+
t.end()
211+
}
212+
}
213+
214+
function resetAgent (cb) {
215+
agent._instrumentation._queue._clear()
216+
agent._instrumentation.currentTransaction = null
217+
agent._httpClient = { request: cb || function () {} }
218+
agent.captureError = function (err) { throw err }
219+
}

0 commit comments

Comments
 (0)