Skip to content

Commit c6eb901

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

File tree

8 files changed

+266
-5
lines changed

8 files changed

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

0 commit comments

Comments
 (0)