Skip to content
This repository was archived by the owner on Aug 26, 2024. It is now read-only.

Commit d609977

Browse files
authored
fix: ApolloFederation::Tracing.should_add_traces() looks for correct headers (Gusto#240)
1 parent 6381140 commit d609977

File tree

11 files changed

+1107
-1314
lines changed

11 files changed

+1107
-1314
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,9 @@ To support [federated tracing](https://www.apollographql.com/docs/apollo-server/
333333
def execute
334334
# ...
335335
context = {
336-
tracing_enabled: ApolloFederation::Tracing.should_add_traces(headers)
336+
# Pass in the headers from your web framework. For Rails this will be request.headers
337+
# but for other frameworks you can pass the Rack env.
338+
tracing_enabled: ApolloFederation::Tracing.should_add_traces(request.headers)
337339
}
338340
# ...
339341
end

example/accounts.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class AccountSchema < GraphQL::Schema
5252
use GraphQL::Execution::Interpreter
5353
use GraphQL::Analysis::AST
5454
end
55+
use ApolloFederation::Tracing
56+
5557
include ApolloFederation::Schema
5658

5759
query(Query)

example/gateway.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
1-
const { ApolloServer } = require('apollo-server');
2-
const { ApolloGateway } = require('@apollo/gateway');
1+
const { ApolloServer } = require('@apollo/server');
2+
const { ApolloGateway, IntrospectAndCompose } = require('@apollo/gateway');
3+
const { startStandaloneServer } = require('@apollo/server/standalone');
34

45
const gateway = new ApolloGateway({
5-
serviceList: [
6-
{ name: 'accounts', url: 'http://localhost:5001/graphql' },
7-
{ name: 'reviews', url: 'http://localhost:5002/graphql' },
8-
{ name: 'products', url: 'http://localhost:5003/graphql' },
9-
{ name: 'inventory', url: 'http://localhost:5004/graphql' },
10-
],
6+
supergraphSdl: new IntrospectAndCompose({
7+
subgraphs: [
8+
{ name: 'accounts', url: 'http://localhost:5001/graphql' },
9+
{ name: 'reviews', url: 'http://localhost:5002/graphql' },
10+
{ name: 'products', url: 'http://localhost:5003/graphql' },
11+
{ name: 'inventory', url: 'http://localhost:5004/graphql' },
12+
],
13+
}),
1114
debug: true,
1215
});
1316

1417
(async () => {
15-
const server = new ApolloServer({ gateway, subscriptions: false, uploads: false });
18+
const server = new ApolloServer({ gateway });
1619

17-
server.listen({ port: 5000 }).then(({ url }) => {
18-
console.log(`🚀 Server ready at ${url}`);
20+
const { url } = await startStandaloneServer(server, {
21+
listen: { port: 5000 },
1922
});
23+
24+
console.log(`🚀 Server ready at: ${url}`);
2025
})();

example/graphql_server.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ def call(env)
6565
query,
6666
operation_name: operation_name,
6767
variables: vars,
68+
context: {
69+
tracing_enabled: ApolloFederation::Tracing.should_add_traces(env),
70+
},
6871
)
6972
['200', { 'Content-Type' => 'application/json' }, [JSON.dump(result.to_h)]]
7073
end

example/inventory.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class InventorySchema < GraphQL::Schema
4646
use GraphQL::Execution::Interpreter
4747
use GraphQL::Analysis::AST
4848
end
49+
use ApolloFederation::Tracing
50+
4951
include ApolloFederation::Schema
5052

5153
orphan_types Product

example/products.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class ProductSchema < GraphQL::Schema
6262
use GraphQL::Execution::Interpreter
6363
use GraphQL::Analysis::AST
6464
end
65+
use ApolloFederation::Tracing
6566

6667
include ApolloFederation::Schema
6768

example/reviews.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class ReviewSchema < GraphQL::Schema
100100
use GraphQL::Execution::Interpreter
101101
use GraphQL::Analysis::AST
102102
end
103+
use ApolloFederation::Tracing
103104

104105
include ApolloFederation::Schema
105106

lib/apollo-federation/tracing.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
module ApolloFederation
44
module Tracing
5+
HEADER_NAME = 'HTTP_APOLLO_FEDERATION_INCLUDE_TRACE'
56
KEY = :ftv1
67
DEBUG_KEY = "#{KEY}_debug".to_sym
78

@@ -12,7 +13,7 @@ def use(schema)
1213
end
1314

1415
def should_add_traces(headers)
15-
headers && headers['apollo-federation-include-trace'] == KEY.to_s
16+
headers && headers[HEADER_NAME] == KEY.to_s
1617
end
1718

1819
# @deprecated There is no need to call this method. Traces are added to the result automatically

package.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,16 @@
1515
"lint": "eslint ."
1616
},
1717
"dependencies": {
18-
"@apollo/gateway": "^0.16.2",
19-
"apollo-server": "^2.14.2",
20-
"graphql": "^14.6.0"
18+
"@apollo/gateway": "^2.4.1",
19+
"@apollo/server": "^4.6.0",
20+
"graphql": "^16.6.0"
2121
},
2222
"devDependencies": {
2323
"@babel/core": "^7.10.1",
2424
"@babel/preset-env": "^7.10.1",
2525
"@semantic-release/changelog": "^5.0.1",
2626
"@semantic-release/exec": "^5.0.0",
2727
"@semantic-release/git": "^9.0.0",
28-
"apollo-server-core": "^2.14.2",
29-
"apollo-server-testing": "^2.14.1",
3028
"babel-jest": "^26.0.1",
3129
"concurrently": "4.1.0",
3230
"eslint-config-gusto": "^10.1.0",

spec/integration/integration.test.js

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { ApolloGateway } from '@apollo/gateway';
2-
import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
3-
import { createTestClient } from 'apollo-server-testing';
1+
import { ApolloGateway, IntrospectAndCompose, RemoteGraphQLDataSource } from '@apollo/gateway';
2+
import { ApolloServer } from '@apollo/server';
3+
import { ApolloServerPluginUsageReporting } from '@apollo/server/plugin/usageReporting';
4+
45
import gql from 'graphql-tag';
56
import { spawn } from 'child_process';
67

@@ -45,7 +46,7 @@ const startService = serviceName =>
4546
});
4647
});
4748

48-
let testClient;
49+
let gatewayServer;
4950
let serviceProcesses = [];
5051
const serviceList = [
5152
{ name: 'accounts', url: 'http://localhost:5001/graphql' },
@@ -57,14 +58,47 @@ const serviceList = [
5758
beforeAll(async () => {
5859
serviceProcesses = await Promise.all(serviceList.map(({ name }) => startService(name)));
5960

60-
const gateway = new ApolloGateway({ serviceList });
61+
const gateway = new ApolloGateway({
62+
supergraphSdl: new IntrospectAndCompose({
63+
subgraphs: serviceList,
64+
}),
65+
buildService({ name, url }) {
66+
return new RemoteGraphQLDataSource({
67+
name,
68+
url,
6169

62-
const server = new ApolloServer({
63-
gateway,
64-
subscriptions: false,
70+
// We can't mock the calls to the Apollo reporting API since they're sent asynchronously
71+
// after query execution completes so instead keep track of any subgraph traces in the
72+
// context.
73+
didReceiveResponse({ response, context }) {
74+
if (context.subgraphTraces && response.extensions && response.extensions.ftv1) {
75+
context.subgraphTraces.push(response.extensions.ftv1);
76+
}
77+
return response;
78+
},
79+
});
80+
},
6581
});
6682

67-
testClient = createTestClient(server);
83+
gatewayServer = new ApolloServer({
84+
gateway,
85+
apollo: {
86+
key: 'My Key',
87+
graphRef: 'My Graph',
88+
},
89+
plugins: [
90+
ApolloServerPluginUsageReporting({
91+
sendReportsImmediately: true,
92+
fetcher: () => {
93+
return {
94+
status: 200,
95+
headers: new Map(),
96+
};
97+
},
98+
fieldLevelInstrumentation: () => true,
99+
}),
100+
],
101+
});
68102
});
69103

70104
afterAll(() => {
@@ -100,7 +134,8 @@ it('works with a gateway', async () => {
100134
}
101135
`;
102136

103-
const result = await testClient.query({ query });
137+
const response = await gatewayServer.executeOperation({ query });
138+
const result = response.body.singleResult;
104139

105140
expect(result.errors).toBeUndefined();
106141
expect(result.data).toEqual({
@@ -164,7 +199,8 @@ it('works with a @requires directive', async () => {
164199
}
165200
`;
166201

167-
const result = await testClient.query({ query });
202+
const response = await gatewayServer.executeOperation({ query });
203+
const result = response.body.singleResult;
168204

169205
expect(result.errors).toBeUndefined();
170206
expect(result.data).toEqual({
@@ -200,7 +236,8 @@ it('works with a @provides directive', async () => {
200236
}
201237
`;
202238

203-
const result = await testClient.query({ query });
239+
const response = await gatewayServer.executeOperation({ query });
240+
const result = response.body.singleResult;
204241

205242
expect(result.errors).toBeUndefined();
206243
expect(result.data).toEqual({
@@ -247,3 +284,23 @@ it('works with a @provides directive', async () => {
247284
],
248285
});
249286
});
287+
288+
it('works with federated traces', async () => {
289+
const query = gql`
290+
{
291+
me {
292+
name
293+
reviews {
294+
body
295+
}
296+
}
297+
}
298+
`;
299+
300+
const contextValue = { subgraphTraces: [] };
301+
const response = await gatewayServer.executeOperation({ query }, { contextValue });
302+
const result = response.body.singleResult;
303+
304+
expect(result.errors).toBeUndefined();
305+
expect(contextValue.subgraphTraces.length).toEqual(2);
306+
});

0 commit comments

Comments
 (0)