Skip to content

Commit 0be1f22

Browse files
committed
feat: initial commit
1 parent d83c33d commit 0be1f22

File tree

7 files changed

+5778
-0
lines changed

7 files changed

+5778
-0
lines changed

.github/workflows/ci.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Node.js CI
2+
3+
on: [push]
4+
5+
jobs:
6+
test:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v2
10+
- name: Use Node.js
11+
uses: actions/setup-node@v1
12+
- run: yarn
13+
- run: yarn build
14+
- run: yarn test
15+
env:
16+
CI: true

package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "apollo-gateway",
3+
"version": "1.0.0",
4+
"main": "dist/cjs/index.js",
5+
"module": "dist/esm/index.js",
6+
"repository": "https://github.com/module-federation/apollo-gateway.git",
7+
"license": "MIT",
8+
"scripts": {
9+
"build": "rollup -c",
10+
"dev": "rollup -c -w",
11+
"test": "jest --coverage"
12+
},
13+
"peerDependencies": {
14+
"@apollo/federation": ">= 0.21.0",
15+
"@apollo/gateway": ">= 0.23.0"
16+
},
17+
"devDependencies": {
18+
"@apollo/federation": "^0.21.0",
19+
"@apollo/gateway": "^0.23.0",
20+
"apollo-graphql": "^0.6.0",
21+
"apollo-server": "^2.21.0",
22+
"apollo-server-testing": "^2.21.0",
23+
"cz-conventional-changelog": "^3.3.0",
24+
"graphql": "^15.5.0",
25+
"jest": "^26.6.3",
26+
"rollup": "^2.39.0"
27+
},
28+
"config": {
29+
"commitizen": {
30+
"path": "cz-conventional-changelog"
31+
}
32+
}
33+
}

rollup.config.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import pkg from "./package.json";
2+
3+
export default [
4+
// CommonJS (for Node) and ES module (for bundlers) build.
5+
// (We could have three entries in the configuration array
6+
// instead of two, but it's quicker to generate multiple
7+
// builds from a single configuration where possible, using
8+
// an array for the `output` option, where we can specify
9+
// `file` and `format` for each target)
10+
{
11+
input: "src/index.js",
12+
external: ["@apollo/federation", "@apollo/gateway"],
13+
output: [
14+
{ file: pkg.main, format: "cjs" },
15+
{ file: pkg.module, format: "es" },
16+
],
17+
},
18+
];

src/federated-gateway.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { buildFederatedSchema } from "@apollo/federation";
2+
import {
3+
ApolloGateway,
4+
LocalGraphQLDataSource,
5+
RemoteGraphQLDataSource,
6+
} from "@apollo/gateway";
7+
8+
/**
9+
* @typedef {import("graphql").DocumentNode} DocumentNode
10+
* @typedef {import("apollo-graphql").GraphQLSchemaModule} GraphQLSchemaModule
11+
* @typedef {import("apollo-graphql").GraphQLResolverMap} GraphQLResolverMap
12+
* @typedef {import("@apollo/gateway").ServiceEndpointDefinition} ServiceEndpointDefinition
13+
*/
14+
15+
/**
16+
* @typedef {object} LegacySchemaModule
17+
* @property {DocumentNode | DocumentNode[]} typeDefs
18+
* @property {GraphQLResolverMap} resolvers
19+
* {
20+
typeDefs: DocumentNode | DocumentNode[];
21+
resolvers?: GraphQLResolverMap<any>;
22+
*/
23+
24+
/**
25+
* @typedef {object} FederatedServiceDefinition
26+
* @property {string} name
27+
* @property {DocumentNode | (GraphQLSchemaModule | DocumentNode)[] | LegacySchemaModule} schema
28+
*/
29+
30+
/**
31+
* @typedef {import("@apollo/gateway").GatewayConfig} FederatedGatewayConfig
32+
* @property {Array<FederatedServiceDefinition | ServiceEndpointDefinition>} serviceList
33+
*/
34+
35+
const DUMMY_URL = "https://";
36+
37+
export class FederatedGateway extends ApolloGateway {
38+
/**
39+
* @param {FederatedGatewayConfig} config
40+
*/
41+
constructor(config) {
42+
if (config.serviceList) {
43+
config.serviceList = config.serviceList.map((service) => {
44+
if (!service.url) {
45+
return {
46+
url: DUMMY_URL,
47+
...service,
48+
};
49+
}
50+
51+
return service;
52+
});
53+
}
54+
55+
super(config);
56+
}
57+
58+
/**
59+
* @param {import("@apollo/gateway").ServiceEndpointDefinition} serviceDef
60+
*/
61+
createDataSource(serviceDef) {
62+
if (serviceDef.url !== DUMMY_URL) {
63+
return new RemoteGraphQLDataSource({ url: serviceDef.url });
64+
}
65+
66+
if (!serviceDef.schema) {
67+
throw new Error(`Invalid service definition for ${serviceDef.name}`);
68+
}
69+
70+
return new LocalGraphQLDataSource(buildFederatedSchema(serviceDef.schema));
71+
}
72+
}

src/federated-gateway.test.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
const { ApolloServer, gql } = require("apollo-server");
2+
const { createTestClient } = require("apollo-server-testing");
3+
4+
const { FederatedGateway } = require("..");
5+
6+
describe("federated-gateway", () => {
7+
let aSchema;
8+
let bSchema;
9+
let gateway;
10+
let server;
11+
beforeEach(() => {
12+
aSchema = {
13+
typeDefs: gql`
14+
type Query {
15+
aPing: String
16+
}
17+
`,
18+
resolvers: {
19+
Query: {
20+
aPing: () => "apong",
21+
},
22+
},
23+
};
24+
25+
bSchema = {
26+
typeDefs: gql`
27+
type Query {
28+
bPing: String
29+
}
30+
`,
31+
resolvers: {
32+
Query: {
33+
bPing: () => "bpong",
34+
},
35+
},
36+
};
37+
38+
gateway = new FederatedGateway({
39+
serviceList: [
40+
{
41+
name: "a",
42+
schema: aSchema,
43+
},
44+
{
45+
name: "b",
46+
schema: bSchema,
47+
},
48+
],
49+
});
50+
server = new ApolloServer({
51+
gateway,
52+
subscriptions: false,
53+
playground: false,
54+
introspection: true,
55+
});
56+
});
57+
58+
it("executes federated service", async () => {
59+
const { query } = createTestClient(server);
60+
61+
const response = await query({
62+
query: `#graphql
63+
{
64+
aPing
65+
bPing
66+
}
67+
`,
68+
});
69+
70+
expect(response.data.aPing).toEqual("apong");
71+
expect(response.data.bPing).toEqual("bpong");
72+
});
73+
});

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { FederatedGateway } from "./federated-gateway";

0 commit comments

Comments
 (0)