-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { createServer, Server } from 'http'; | ||
import { getStitchedSchemaFromSupergraphSdl } from '@graphql-tools/federation'; | ||
import { createYoga, YogaServerInstance } from 'graphql-yoga'; | ||
import { TestSubgraph1 } from './TestSubgraph1'; | ||
import { TestSubgraph2 } from './TestSubgraph2'; | ||
|
||
export class TestEnvironment { | ||
public readonly subgraph1: TestSubgraph1 = new TestSubgraph1(); | ||
public readonly subgraph2: TestSubgraph2 = new TestSubgraph2(); | ||
private yogaGateway?: Server; | ||
#yoga?: YogaServerInstance<Record<string, unknown>, Record<string, unknown>>; | ||
public get yoga() { | ||
if (!this.#yoga) { | ||
throw Error('You have to start test environment first!'); | ||
} | ||
|
||
return this.#yoga; | ||
} | ||
|
||
public async start(): Promise<void> { | ||
// start subgraphs | ||
await Promise.all([this.subgraph1.start(), this.subgraph2.start()]); | ||
|
||
// dynamic import is used only due to incompatibility with graphql@15 | ||
const { IntrospectAndCompose, RemoteGraphQLDataSource } = await import( | ||
'@apollo/gateway' | ||
); | ||
const { supergraphSdl } = await new IntrospectAndCompose({ | ||
subgraphs: [ | ||
{ | ||
name: 'subgraph1', | ||
url: `http://localhost:${this.subgraph1.port}/graphql`, | ||
}, | ||
{ | ||
name: 'subgraph2', | ||
url: `http://localhost:${this.subgraph2.port}/graphql`, | ||
}, | ||
], | ||
}).initialize({ | ||
healthCheck: async () => Promise.resolve(), | ||
update: () => undefined, | ||
getDataSource: ({ url }) => new RemoteGraphQLDataSource({ url }), | ||
}); | ||
|
||
// compose stitched schema | ||
const schema = getStitchedSchemaFromSupergraphSdl({ supergraphSdl }); | ||
|
||
// start yoga geteway | ||
this.yoga = createYoga({ schema, maskedErrors: false }); | ||
Check failure on line 49 in packages/federation/tests/unavailable-subgraph/fixtures/TestEnvironment.ts
|
||
this.yogaGateway = createServer(this.yoga); | ||
await new Promise<void>((resolve) => | ||
this.yogaGateway?.listen(this.getTestPort(), () => resolve()), | ||
); | ||
} | ||
|
||
public async stop(): Promise<void> { | ||
// stop yoga geteway | ||
await new Promise<void>((resolve, reject) => | ||
this.yogaGateway?.close((error) => (error ? reject(error) : resolve())), | ||
); | ||
// stop subgraphs | ||
await Promise.all([this.subgraph1.stop(), this.subgraph2.stop()]); | ||
} | ||
|
||
public getTestPort(): number { | ||
return parseInt(process.env['VITEST_POOL_ID'] ?? '1') + 3000; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { createServer, Server } from 'http'; | ||
import { parse } from 'graphql'; | ||
import { createGraphQLError, createYoga } from 'graphql-yoga'; | ||
|
||
const typeDefs = parse(/* GraphQL */ ` | ||
type Query { | ||
testNestedField: TestNestedField! | ||
} | ||
type TestNestedField { | ||
subgraph1_Nullable: TestSubgraph1Query | ||
subgraph1_NonNullable: TestSubgraph1Query! | ||
} | ||
type TestSubgraph1Query { | ||
testSuccessQuery: TestUser1 | ||
testErrorQuery: TestUser1 | ||
} | ||
type TestUser1 { | ||
id: String! | ||
email: String! | ||
sub1: Boolean! | ||
} | ||
`); | ||
|
||
const resolvers = { | ||
Query: { | ||
testNestedField: () => ({ | ||
subgraph1_Nullable: () => ({ | ||
testSuccessQuery: () => { | ||
return { | ||
id: 'user1', | ||
email: '[email protected]', | ||
sub1: true, | ||
}; | ||
}, | ||
testErrorQuery: () => { | ||
throw createGraphQLError('My original subgraph1 error!', { | ||
extensions: { | ||
code: 'BAD_REQUEST', | ||
}, | ||
}); | ||
}, | ||
}), | ||
subgraph1_NonNullable: () => ({ | ||
testSuccessQuery: () => { | ||
return { | ||
id: 'user1', | ||
email: '[email protected]', | ||
sub1: true, | ||
}; | ||
}, | ||
testErrorQuery: () => { | ||
throw createGraphQLError('My original subgraph1 error!', { | ||
extensions: { | ||
code: 'BAD_REQUEST', | ||
}, | ||
}); | ||
}, | ||
}), | ||
}), | ||
}, | ||
}; | ||
|
||
export class TestSubgraph1 { | ||
public readonly port: number = this.getTestPort() + 1000; | ||
private subgraph?: Server; | ||
|
||
public async start(): Promise<void> { | ||
// dynamic import is used only due to incompatibility with graphql@15 | ||
const { buildSubgraphSchema } = await import('@apollo/subgraph'); | ||
const yoga = createYoga({ | ||
schema: buildSubgraphSchema({ typeDefs, resolvers }), | ||
}); | ||
this.subgraph = createServer(yoga); | ||
|
||
return new Promise<void>((resolve) => { | ||
this.subgraph?.listen(this.port, () => resolve()); | ||
}); | ||
} | ||
|
||
public async stop(): Promise<void> { | ||
return new Promise<void>((resolve, reject) => { | ||
this.subgraph?.close((error) => (error ? reject(error) : resolve())); | ||
}); | ||
} | ||
|
||
private getTestPort(): number { | ||
return parseInt(process.env['VITEST_POOL_ID'] ?? '1') + 3000; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { createServer, Server } from 'http'; | ||
import { parse } from 'graphql'; | ||
import { createGraphQLError, createYoga } from 'graphql-yoga'; | ||
|
||
const typeDefs = parse(/* GraphQL */ ` | ||
type Query { | ||
testNestedField: TestNestedField! | ||
} | ||
type TestNestedField { | ||
subgraph2_Nullable: TestSubgraph2Query | ||
subgraph2_NonNullable: TestSubgraph2Query! | ||
} | ||
type TestSubgraph2Query { | ||
testSuccessQuery: TestUser2 | ||
testErrorQuery: TestUser2 | ||
} | ||
type TestUser2 { | ||
id: String! | ||
email: String! | ||
sub2: Boolean! | ||
} | ||
`); | ||
|
||
const resolvers = { | ||
Query: { | ||
testNestedField: () => ({ | ||
subgraph2_Nullable: () => ({ | ||
testSuccessQuery: () => { | ||
return { | ||
id: 'user2', | ||
email: '[email protected]', | ||
sub2: true, | ||
}; | ||
}, | ||
testErrorQuery: () => { | ||
throw createGraphQLError('My original subgraph2 error!', { | ||
extensions: { | ||
code: 'BAD_REQUEST', | ||
}, | ||
}); | ||
}, | ||
}), | ||
subgraph2_NonNullable: () => ({ | ||
testSuccessQuery: () => { | ||
return { | ||
id: 'user2', | ||
email: '[email protected]', | ||
sub2: true, | ||
}; | ||
}, | ||
testErrorQuery: () => { | ||
throw createGraphQLError('My original subgraph2 error!', { | ||
extensions: { | ||
code: 'BAD_REQUEST', | ||
}, | ||
}); | ||
}, | ||
}), | ||
}), | ||
}, | ||
}; | ||
|
||
export class TestSubgraph2 { | ||
public readonly port: number = this.getTestPort() + 2000; | ||
private subgraph?: Server; | ||
|
||
public async start(): Promise<void> { | ||
// dynamic import is used only due to incompatibility with graphql@15 | ||
const { buildSubgraphSchema } = await import('@apollo/subgraph'); | ||
const yoga = createYoga({ | ||
schema: buildSubgraphSchema({ typeDefs, resolvers }), | ||
}); | ||
this.subgraph = createServer(yoga); | ||
|
||
return new Promise<void>((resolve) => { | ||
this.subgraph?.listen(this.port, () => resolve()); | ||
}); | ||
} | ||
|
||
public async stop(): Promise<void> { | ||
return new Promise<void>((resolve, reject) => { | ||
this.subgraph?.close((error) => (error ? reject(error) : resolve())); | ||
}); | ||
} | ||
|
||
private getTestPort(): number { | ||
return parseInt(process.env['VITEST_POOL_ID'] ?? '1') + 3000; | ||
} | ||
} |