Skip to content

Commit

Permalink
V0: Change dependency on Apollo Server 3 to new type package
Browse files Browse the repository at this point in the history
Backport of #2044.

This removes the last dependency on Apollo Server from Apollo Gateway.

Part of apollographql/apollo-server#6057 and
apollographql/apollo-server#6719

The new package `@apollo/server-gateway-interface` (in the apollo-utils
repo for now, though it should move to the apollo-server repo once AS4
is fully released) defines types that are pretty close to compatible
with the AS3 types previously used here, but don't require a dependency
on the entirety of AS3. This new package will be used by AS4 and AS4
will convert its data into the format described by these types.

This change is entirely a build-time change (other than a slight change
to how a enum is referenced). So the worst case scenario if this differs
unintentionally from the original AS3 definitions is that users can
apply a bit of `as any` to fix it.

Note that we've removed some `<TContext>` from types that it turned out
were only ever instantiated with `Record<string, any>` anyway. They are
left in in RemoteGraphQLDataSource because users making their own data
sources can explicitly specify their context type.

The types in `@apollo/server-gateway-interface` are pretty close to the
AS3 types (with different names) but there are some slight differences.
The cache scope enum is replaced with `any`, as enums are not
structurally typed and it is otherwise difficult to type them.
`GatewayInterface` now expects `onSchemaLoadOrUpdate` to exist and
doesn't mention the old `onSchemaChange`.

For now, we leave `apollo-reporting-protobuf` alone, so we don't have a direct
dependency on a prerelease.
  • Loading branch information
glasser committed Aug 8, 2022
1 parent d988a4c commit 746fb16
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 110 deletions.
3 changes: 1 addition & 2 deletions gateway-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@
"@apollo/core-schema": "^0.2.0",
"@apollo/federation": "file:../federation-js",
"@apollo/query-planner": "file:../query-planner-js",
"@apollo/server-gateway-interface": "^1.0.0",
"@apollo/utils.createhash": "^1.0.0",
"@apollo/utils.fetcher": "^1.0.0",
"@apollo/utils.logger": "^1.0.0",
"@josephg/resolvable": "^1.0.1",
"@opentelemetry/api": "^1.0.1",
"@types/node-fetch": "2.6.2",
"apollo-reporting-protobuf": "^0.8.0 || ^3.0.0",
"apollo-server-core": "^2.23.0 || ^3.0.0",
"apollo-server-types": "^0.9.0 || ^3.0.0",
"async-retry": "^1.3.3",
"loglevel": "^1.6.1",
"lru-cache": "^7.13.1",
Expand Down
12 changes: 7 additions & 5 deletions gateway-js/src/__tests__/executeQueryPlan.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import {
buildClientSchema,
getIntrospectionQuery,
GraphQLError,
GraphQLObjectType,
GraphQLSchema,
print,
} from 'graphql';
import gql from 'graphql-tag';
import { GraphQLRequestContext, VariableValues } from 'apollo-server-types';
import { AuthenticationError } from 'apollo-server-core';
import { buildOperationContext } from '../operationContext';
import { executeQueryPlan } from '../executeQueryPlan';
import { LocalGraphQLDataSource } from '../datasources/LocalGraphQLDataSource';
Expand All @@ -21,6 +20,7 @@ import { ApolloGateway } from '..';
import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
import { getFederatedTestingSchema } from './execution-utils';
import { addResolversToSchema, GraphQLResolverMap } from '@apollo/subgraph/src/schema-helper';
import {GatewayGraphQLRequestContext} from '@apollo/server-gateway-interface';

expect.addSnapshotSerializer(astSerializer);
expect.addSnapshotSerializer(queryPlanSerializer);
Expand Down Expand Up @@ -53,8 +53,8 @@ describe('executeQueryPlan', () => {
});

function buildRequestContext(
variables: VariableValues = {},
): GraphQLRequestContext {
variables: Record<string, any> = {},
): GatewayGraphQLRequestContext {
// @ts-ignore
return {
cache: undefined as any,
Expand Down Expand Up @@ -101,7 +101,9 @@ describe('executeQueryPlan', () => {
overrideResolversInService('accounts', {
RootQuery: {
me() {
throw new AuthenticationError('Something went wrong');
throw new GraphQLError('Something went wrong', {
extensions: { code: 'UNAUTHENTICATED' },
});
},
},
});
Expand Down
6 changes: 3 additions & 3 deletions gateway-js/src/__tests__/execution-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
GraphQLSchemaModule,
GraphQLResolverMap,
} from '@apollo/subgraph/src/schema-helper';
import { GraphQLRequest, GraphQLExecutionResult } from 'apollo-server-types';
import type { Logger } from '@apollo/utils.logger';
import {
composeAndValidate,
Expand All @@ -22,6 +21,7 @@ import { queryPlanSerializer, astSerializer } from 'apollo-federation-integratio
import gql from 'graphql-tag';
import { fixtures } from 'apollo-federation-integration-testsuite';
import { parse } from 'graphql';
import { GatewayExecutionResult, GatewayGraphQLRequest } from '@apollo/server-gateway-interface';

const prettyFormat = require('pretty-format');

Expand All @@ -39,10 +39,10 @@ export function overrideResolversInService(
}

export async function execute(
request: GraphQLRequest,
request: GatewayGraphQLRequest,
services: ServiceDefinitionModule[] = fixtures,
logger: Logger = console,
): Promise<GraphQLExecutionResult & { queryPlan: QueryPlan }> {
): Promise<GatewayExecutionResult & { queryPlan: QueryPlan }> {
const serviceMap = Object.fromEntries(
services.map(({ name, typeDefs, resolvers }) => {
return [
Expand Down
4 changes: 2 additions & 2 deletions gateway-js/src/__tests__/gateway/executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('ApolloGateway executor', () => {
variables: { first: '3' },
},
queryHash: 'hashed',
context: null,
context: {},
cache: {} as any,
logger,
});
Expand Down Expand Up @@ -83,7 +83,7 @@ describe('ApolloGateway executor', () => {
document: gql(source),
request: {},
queryHash: 'hashed',
context: null,
context: {},
cache: {} as any,
logger,
});
Expand Down
6 changes: 2 additions & 4 deletions gateway-js/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GraphQLError, GraphQLSchema } from 'graphql';
import { HeadersInit } from 'node-fetch';
import { GraphQLRequestContextExecutionDidStart } from 'apollo-server-types';
import { GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
import type { Logger } from '@apollo/utils.logger';
import { ServiceDefinition } from '@apollo/federation';
import { GraphQLDataSource } from './datasources/types';
Expand All @@ -20,9 +20,7 @@ export type Experimental_DidResolveQueryPlanCallback = ({
readonly queryPlan: QueryPlan;
readonly serviceMap: ServiceMap;
readonly operationContext: OperationContext;
readonly requestContext: GraphQLRequestContextExecutionDidStart<
Record<string, any>
>;
readonly requestContext: GatewayGraphQLRequestContext;
}) => void;

interface ImplementingServiceLocation {
Expand Down
4 changes: 2 additions & 2 deletions gateway-js/src/datasources/LocalGraphQLDataSource.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GraphQLResponse } from 'apollo-server-types';
import { GatewayGraphQLResponse } from '@apollo/server-gateway-interface';
import {
GraphQLSchema,
graphql,
Expand All @@ -18,7 +18,7 @@ export class LocalGraphQLDataSource<
async process({
request,
context,
}: GraphQLDataSourceProcessOptions<TContext>): Promise<GraphQLResponse> {
}: GraphQLDataSourceProcessOptions<TContext>): Promise<GatewayGraphQLResponse> {
return graphql({
schema: this.schema,
source: request.query!,
Expand Down
38 changes: 15 additions & 23 deletions gateway-js/src/datasources/RemoteGraphQLDataSource.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
import {
GraphQLRequestContext,
GraphQLResponse,
ValueOrPromise,
GraphQLRequest,
CacheHint,
CacheScope,
CachePolicy,
} from 'apollo-server-types';
import { isObject } from '../utilities/predicates';
import { GraphQLDataSource, GraphQLDataSourceProcessOptions, GraphQLDataSourceRequestKind } from './types';
import { createHash } from '@apollo/utils.createhash';
Expand All @@ -15,6 +6,7 @@ import fetcher from 'make-fetch-happen';
import { Headers as NodeFetchHeaders, Request as NodeFetchRequest } from 'node-fetch';
import { Fetcher, FetcherRequestInit, FetcherResponse } from '@apollo/utils.fetcher';
import { GraphQLError, GraphQLErrorExtensions } from 'graphql';
import { GatewayCacheHint, GatewayCachePolicy, GatewayGraphQLRequest, GatewayGraphQLRequestContext, GatewayGraphQLResponse } from '@apollo/server-gateway-interface';

export class RemoteGraphQLDataSource<
TContext extends Record<string, any> = Record<string, any>,
Expand Down Expand Up @@ -73,7 +65,7 @@ export class RemoteGraphQLDataSource<

async process(
options: GraphQLDataSourceProcessOptions<TContext>,
): Promise<GraphQLResponse> {
): Promise<GatewayGraphQLResponse> {
const { request, context: originalContext } = options;
// Deal with a bit of a hairy situation in typings: when doing health checks
// and schema checks we always pass in `{}` as the context even though it's
Expand Down Expand Up @@ -157,7 +149,7 @@ export class RemoteGraphQLDataSource<
// If APQ was enabled, we'll run the same request again, but add in the
// previously omitted `query`. If APQ was NOT enabled, this is the first
// request (non-APQ, all the way).
const requestWithQuery: GraphQLRequest = {
const requestWithQuery: GatewayGraphQLRequest = {
query,
...requestWithoutQuery,
};
Expand All @@ -171,9 +163,9 @@ export class RemoteGraphQLDataSource<
}

private async sendRequest(
request: GraphQLRequest,
request: GatewayGraphQLRequest,
context: TContext,
): Promise<GraphQLResponse> {
): Promise<GatewayGraphQLResponse> {
// This would represent an internal programming error since this shouldn't
// be possible in the way that this method is invoked right now.
if (!request.http) {
Expand Down Expand Up @@ -226,19 +218,19 @@ export class RemoteGraphQLDataSource<

public willSendRequest?(
options: GraphQLDataSourceProcessOptions<TContext>,
): ValueOrPromise<void>;
): void | Promise<void>;

private async respond({
response,
request,
context,
overallCachePolicy,
}: {
response: GraphQLResponse;
request: GraphQLRequest;
response: GatewayGraphQLResponse;
request: GatewayGraphQLRequest;
context: TContext;
overallCachePolicy: CachePolicy | null;
}): Promise<GraphQLResponse> {
overallCachePolicy: GatewayCachePolicy | null;
}): Promise<GatewayGraphQLResponse> {
const processedResponse =
typeof this.didReceiveResponse === 'function'
? await this.didReceiveResponse({ response, request, context })
Expand All @@ -253,16 +245,16 @@ export class RemoteGraphQLDataSource<
// thus the overall response) is uncacheable. (If you don't like this, you
// can tweak the `cache-control` header in your `didReceiveResponse`
// method.)
const hint: CacheHint = { maxAge: 0 };
const hint: GatewayCacheHint = { maxAge: 0 };
const maxAge = parsed['max-age'];
if (typeof maxAge === 'string' && maxAge.match(/^[0-9]+$/)) {
hint.maxAge = +maxAge;
}
if (parsed['private'] === true) {
hint.scope = CacheScope.Private;
hint.scope = 'PRIVATE';
}
if (parsed['public'] === true) {
hint.scope = CacheScope.Public;
hint.scope = 'PUBLIC';
}
overallCachePolicy.restrict(hint);
}
Expand All @@ -272,9 +264,9 @@ export class RemoteGraphQLDataSource<

public didReceiveResponse?(
requestContext: Required<
Pick<GraphQLRequestContext<TContext>, 'request' | 'response' | 'context'>
Pick<GatewayGraphQLRequestContext<TContext>, 'request' | 'response' | 'context'>
>,
): ValueOrPromise<GraphQLResponse>;
): GatewayGraphQLResponse | Promise<GatewayGraphQLResponse>;

public didEncounterError(
error: Error,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { LocalGraphQLDataSource } from '../LocalGraphQLDataSource';
import { buildSubgraphSchema } from '@apollo/subgraph';
import gql from 'graphql-tag';
import { GraphQLResolverMap } from '@apollo/subgraph/src/schema-helper';
import { GraphQLRequestContext } from 'apollo-server-types';
import { GraphQLDataSourceRequestKind } from '../types';
import { GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';

describe('constructing requests', () => {
it('accepts context', async () => {
Expand Down Expand Up @@ -42,7 +42,7 @@ describe('constructing requests', () => {
},
incomingRequestContext: {
context: { userId: 2 },
} as GraphQLRequestContext<{userId: number}>,
} as GatewayGraphQLRequestContext<{userId: number}>,
context: { userId: 2 },
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { RemoteGraphQLDataSource } from '../RemoteGraphQLDataSource';
import { Response, Headers } from 'node-fetch';
import { GraphQLRequestContext } from 'apollo-server-types';
import { GraphQLDataSourceRequestKind } from '../types';
import { nockBeforeEach, nockAfterEach } from '../../__tests__/nockAssertions';
import nock from 'nock';
import { GraphQLError } from 'graphql';
import { GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';

beforeEach(nockBeforeEach);
afterEach(nockAfterEach);
Expand Down Expand Up @@ -317,7 +317,7 @@ describe('didReceiveResponse', () => {
response,
}: Required<
Pick<
GraphQLRequestContext<MyContext>,
GatewayGraphQLRequestContext<MyContext>,
'request' | 'response' | 'context'
>
>) {
Expand Down Expand Up @@ -362,7 +362,7 @@ describe('didReceiveResponse', () => {
response,
}: Required<
Pick<
GraphQLRequestContext<MyContext>,
GatewayGraphQLRequestContext<MyContext>,
'request' | 'response' | 'context'
>
>) {
Expand Down Expand Up @@ -398,7 +398,7 @@ describe('didReceiveResponse', () => {
response,
}: Required<
Pick<
GraphQLRequestContext<MyContext>,
GatewayGraphQLRequestContext<MyContext>,
'request' | 'response' | 'context'
>
>) {
Expand Down Expand Up @@ -452,7 +452,7 @@ describe('didEncounterError', () => {
},
incomingRequestContext: {
context,
} as GraphQLRequestContext<MyContext>,
} as GatewayGraphQLRequestContext<MyContext>,
context,
});

Expand Down
12 changes: 6 additions & 6 deletions gateway-js/src/datasources/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { GraphQLResponse, GraphQLRequestContext } from 'apollo-server-types';
import { GatewayGraphQLResponse, GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';

export interface GraphQLDataSource<
TContext extends Record<string, any> = Record<string, any>,
> {
process(
options: GraphQLDataSourceProcessOptions<TContext>,
): Promise<GraphQLResponse>;
): Promise<GatewayGraphQLResponse>;
}

export enum GraphQLDataSourceRequestKind {
Expand All @@ -20,7 +20,7 @@ export type GraphQLDataSourceProcessOptions<
/**
* The request to send to the subgraph.
*/
request: GraphQLRequestContext<TContext>['request'];
request: GatewayGraphQLRequestContext<TContext>['request'];
} & (
| {
kind: GraphQLDataSourceRequestKind.INCOMING_OPERATION;
Expand All @@ -33,10 +33,10 @@ export type GraphQLDataSourceProcessOptions<
* to be treated as optional.
*/
incomingRequestContext: Omit<
GraphQLRequestContext<TContext>,
GatewayGraphQLRequestContext<TContext>,
'overallCachePolicy'
> &
Pick<Partial<GraphQLRequestContext<TContext>>, 'overallCachePolicy'>;
Pick<Partial<GatewayGraphQLRequestContext<TContext>>, 'overallCachePolicy'>;
/**
* Equivalent to incomingRequestContext.context (provided here for
* backwards compatibility): the object created by the Apollo Server
Expand All @@ -45,7 +45,7 @@ export type GraphQLDataSourceProcessOptions<
* @deprecated Use `incomingRequestContext.context` instead (after
* checking `kind`).
*/
context: GraphQLRequestContext<TContext>['context'];
context: GatewayGraphQLRequestContext<TContext>['context'];
}
| {
kind:
Expand Down

0 comments on commit 746fb16

Please sign in to comment.