diff --git a/CHANGELOG.md b/CHANGELOG.md index b39cbb83c0d..accc8c8a789 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ - `apollo-datasource-rest`: Correctly allow a TTL value of `0` to represent "not-cacheable". [PR #2588](https://github.com/apollographql/apollo-server/pull/2588) - `apollo-datasource-rest`: Fix `Invalid argument` in IE11, when `this.headers` is `undefined`. [PR #2607](https://github.com/apollographql/apollo-server/pull/2607) +- Don't add `cacheControl` directive if one has already been defined. [PR #2428](https://github.com/apollographql/apollo-server/pull/2428) + ### v2.4.8 - No functional changes in this version. The patch version has been bumped to fix the `README.md` displayed on the [npm package for `apollo-server`](https://npm.im/apollo-server) as a result of a broken publish. Apologies for the additional noise! diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index e8304035ded..4a9147f9e99 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -51,6 +51,7 @@ import { } from './playground'; import { generateSchemaHash } from './utils/schemaHash'; +import { isDirectiveDefined } from './utils/isDirectiveDefined'; import { processGraphQLRequest, GraphQLRequestContext, @@ -270,19 +271,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'); diff --git a/packages/apollo-server-core/src/__tests__/isDirectiveDefined.ts b/packages/apollo-server-core/src/__tests__/isDirectiveDefined.ts new file mode 100644 index 00000000000..e3353064f7b --- /dev/null +++ b/packages/apollo-server-core/src/__tests__/isDirectiveDefined.ts @@ -0,0 +1,44 @@ +import { gql } from '../'; +import { isDirectiveDefined } from '../utils/isDirectiveDefined'; + +describe('isDirectiveDefined', () => { + it('returns false when a directive is not defined', () => { + expect( + isDirectiveDefined( + [ + gql` + type Query { + hello: String + } + `, + ], + 'cacheControl', + ), + ).toBe(false); + }); + + it('returns true when a directive is defined', () => { + expect( + isDirectiveDefined( + [ + gql` + type Query { + hello: String + } + + enum CacheControlScope { + PUBLIC + PRIVATE + } + + directive @cacheControl( + maxAge: Int + scope: CacheControlScope + ) on FIELD_DEFINITION | OBJECT | INTERFACE + `, + ], + 'cacheControl', + ), + ).toBe(true); + }); +}); diff --git a/packages/apollo-server-core/src/utils/isDirectiveDefined.ts b/packages/apollo-server-core/src/utils/isDirectiveDefined.ts new file mode 100644 index 00000000000..4523573b851 --- /dev/null +++ b/packages/apollo-server-core/src/utils/isDirectiveDefined.ts @@ -0,0 +1,13 @@ +import { DocumentNode, Kind } from 'graphql/language'; + +export const isDirectiveDefined = ( + typeDefs: DocumentNode[], + directiveName: string, +) => + typeDefs.some(typeDef => + typeDef.definitions.some( + definition => + definition.kind === Kind.DIRECTIVE_DEFINITION && + definition.name.value === directiveName, + ), + );