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

Add support for inherit key on cacheControl. #1424

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/apollo-cache-control/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

### Unreleased

* Add support for `inherited` key to skip setting a `maxAge` to 0 if a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lunks I wonder if it would provide more utility for inherit to be recursive, such that subfields will also inherit their cacheability as well from grandparents, etc. Basically, should inherit itself be inherited?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly what you're proposing, I don't think that's a good idea, because it would mark an entire tree as cacheable. And I think objects should always have to opt-in to caching (even if that's through inherit).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a bit of background on why I think inheritance should be opt-in, see here:
https://github.com/apollographql/apollo-cache-control-js/issues/14#issuecomment-37315847.6

type has no explicit `maxAge` set.

### v0.1.1

* Fix `defaultMaxAge` feature (introduced in 0.1.0) so that `maxAge: 0` overrides the default, as previously documented.
Expand Down
111 changes: 111 additions & 0 deletions packages/apollo-cache-control/src/__tests__/cacheControlDirective.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { buildSchema } from 'graphql';
import { makeExecutableSchema } from 'graphql-tools';

import { CacheScope } from '../';
import { collectCacheControlHints } from './test-utils/helpers';
Expand Down Expand Up @@ -226,4 +227,114 @@ describe('@cacheControl directives', () => {
scope: CacheScope.Private,
});
});

it('should use cache from parent if inherit is true', async () => {
const typeDefs = `
type Query {
droids(page: Int = 0): DroidList @cacheControl(maxAge: 10)
}

type DroidList @cacheControl(inherit: true) {
edges: [DroidEdge!]
}

type DroidEdge @cacheControl(inherit: true) {
node: Droid!
}

type Droid @cacheControl(inherit: true) {
id: ID!
name: String!
}
`;

const resolvers = {
Query: {
droids: (_source, _args, _context, { cacheControl }) => {
return {
edges: [{ node: { id: 1 } }],
};
},
},
};

const schema = makeExecutableSchema({ typeDefs, resolvers });

const hints = await collectCacheControlHints(
schema,
`
query {
droids {
edges {
node {
id
}
}
}
}
`,
{ defaultMaxAge: 0 },
);

expect(hints).toEqual([{ maxAge: 10, path: ['droids'], scope: undefined }]);
});

it('should not use cache from parent if inherit is false', async () => {
const typeDefs = `
type Query {
droids(page: Int = 0): DroidList @cacheControl(maxAge: 10)
}

type DroidList @cacheControl(inherit: true) {
edges: [DroidEdge!]
}

type DroidEdge @cacheControl(inherit: false) {
node: Droid!
}

type Droid @cacheControl(inherit: true) {
id: ID!
name: String!
}
`;

const resolvers = {
Query: {
droids: (_source, _args, _context, { cacheControl }) => {
return {
edges: [{ node: { id: 1 } }],
};
},
},
};

const schema = makeExecutableSchema({ typeDefs, resolvers });

const hints = await collectCacheControlHints(
schema,
`
query {
droids {
edges {
node {
id
}
}
}
}
`,
{ defaultMaxAge: 0 },
);

expect(hints).toEqual([
{ inherit: undefined, maxAge: 10, path: ['droids'], scope: undefined },
{
inherit: false,
maxAge: 0,
path: ['droids', 'edges'],
scope: undefined,
},
]);
});
});
18 changes: 15 additions & 3 deletions packages/apollo-cache-control/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface CacheControlFormat {
export interface CacheHint {
maxAge?: number;
scope?: CacheScope;
inherit?: boolean;
}

export enum CacheScope {
Expand Down Expand Up @@ -86,12 +87,13 @@ export class CacheControlExtension<TContext = any>

// If this resolver returns an object and we haven't seen an explicit maxAge
// hint, set the maxAge to 0 (uncached) or the default if specified in the
// constructor. (Non-object fields by default are assumed to inherit their
// cacheability from their parents.)
// constructor unless it inherits from parent. (Non-object fields by default
// are assumed to inherit their cacheability from their parents.)
if (
(targetType instanceof GraphQLObjectType ||
targetType instanceof GraphQLInterfaceType) &&
hint.maxAge === undefined
hint.maxAge === undefined &&
!hint.inherit
) {
hint.maxAge = this.defaultMaxAge;
}
Expand Down Expand Up @@ -149,6 +151,9 @@ function cacheHintFromDirectives(
const scopeArgument = cacheControlDirective.arguments.find(
argument => argument.name.value === 'scope',
);
const inheritArgument = cacheControlDirective.arguments.find(
argument => argument.name.value === 'inherit',
);

// TODO: Add proper typechecking of arguments
return {
Expand All @@ -164,6 +169,12 @@ function cacheHintFromDirectives(
scopeArgument.value.kind === 'EnumValue'
? (scopeArgument.value.value as CacheScope)
: undefined,
inherit:
inheritArgument &&
inheritArgument.value &&
inheritArgument.value.kind === 'BooleanValue'
? (inheritArgument.value.value as boolean)
: undefined,
};
}

Expand All @@ -176,5 +187,6 @@ function mergeHints(
return {
maxAge: otherHint.maxAge !== undefined ? otherHint.maxAge : hint.maxAge,
scope: otherHint.scope || hint.scope,
inherit: otherHint.inherit,
};
}