Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid duplicate cacheControl directives via isDirectiveDefined (redo) #2762

Merged
merged 8 commits into from
Jun 25, 2019
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The version headers in this history reflect the versions of Apollo Server itself

> The changes noted within this `vNEXT` section have not been released yet. New PRs and commits which introduce changes should include an entry in this `vNEXT` section as part of their development. When a release is being prepared, a new header will be (manually) created below and the the appropriate changes within that release will be moved into the new section.

- `apollo-server-core`: Avoid duplicate `cacheControl` directives being added via `isDirectiveDefined`. [PR #2762](https://github.com/apollographql/apollo-server/pull/2762) (was reverted in 2.6.1 because previous implementation released in 2.6.0 broke passing in typedefs as a string)

### v2.6.5

- `apollo-engine-reporting`: Simplify the technique for capturing `operationName`. [PR #2899](https://github.com/apollographql/apollo-server/pull/2899)
Expand Down
30 changes: 17 additions & 13 deletions packages/apollo-server-core/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ import {
PlaygroundRenderPageOptions,
} from './playground';

import createSHA from './utils/createSHA';
import { generateSchemaHash } from './utils/schemaHash';
import { isDirectiveDefined } from './utils/isDirectiveDefined';
import createSHA from './utils/createSHA';
import {
processGraphQLRequest,
GraphQLRequestContext,
Expand Down Expand Up @@ -272,19 +273,22 @@ export class ApolloServerBase {

// We augment the typeDefs with the @cacheControl directive and associated
// scope enum, so makeExecutableSchema won't fail SDL validation
augmentedTypeDefs.push(
gql`
enum CacheControlScope {
PUBLIC
PRIVATE
}

directive @cacheControl(
maxAge: Int
scope: CacheControlScope
) on FIELD_DEFINITION | OBJECT | INTERFACE
`,
);
if (!isDirectiveDefined(augmentedTypeDefs, 'cacheControl')) {
augmentedTypeDefs.push(
gql`
enum CacheControlScope {
PUBLIC
PRIVATE
}

directive @cacheControl(
maxAge: Int
scope: CacheControlScope
) on FIELD_DEFINITION | OBJECT | INTERFACE
`,
);
}

if (this.uploadsConfig) {
const { GraphQLUpload } = require('graphql-upload');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { gql } from '../';
import { isDirectiveDefined } from '../utils/isDirectiveDefined';

describe('isDirectiveDefined', () => {
const noCacheControl = `
type Query {
hello: String
}
`;
const hasCacheControl = `
type Query {
hello: String
}

enum CacheControlScope {
PUBLIC
PRIVATE
}

directive @cacheControl(
maxAge: Int
scope: CacheControlScope
) on FIELD_DEFINITION | OBJECT | INTERFACE
`;

describe('When passed an array of DocumentNode', () => {
it('returns false when a directive is not defined', () => {
expect(isDirectiveDefined([gql(noCacheControl)], 'cacheControl')).toBe(
false,
);
});
it('returns true when a directive is defined', () => {
expect(isDirectiveDefined([gql(hasCacheControl)], 'cacheControl')).toBe(
true,
);
});
});

describe('When passed a string', () => {
it('returns false when a directive is not defined', () => {
expect(isDirectiveDefined(noCacheControl, 'cacheControl')).toBe(false);
});
it('returns true when a directive is defined', () => {
expect(isDirectiveDefined(hasCacheControl, 'cacheControl')).toBe(true);
});
});
});
18 changes: 18 additions & 0 deletions packages/apollo-server-core/src/utils/isDirectiveDefined.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DocumentNode, Kind } from 'graphql/language';
import { gql } from '../';

export const isDirectiveDefined = (
typeDefs: DocumentNode[] | string,
directiveName: string,
): boolean => {
if (typeof typeDefs === 'string') {
return isDirectiveDefined([gql(typeDefs)], directiveName);
}
return typeDefs.some(typeDef =>
typeDef.definitions.some(
definition =>
definition.kind === Kind.DIRECTIVE_DEFINITION &&
definition.name.value === directiveName,
),
);
};