diff --git a/packages/plugins/response-cache/__tests__/response-cache.spec.ts b/packages/plugins/response-cache/__tests__/response-cache.spec.ts new file mode 100644 index 0000000000..72094950ce --- /dev/null +++ b/packages/plugins/response-cache/__tests__/response-cache.spec.ts @@ -0,0 +1,122 @@ +import { createYoga } from 'graphql-yoga' +import { useResponseCache } from '@graphql-yoga/plugin-response-cache' + +it('cache a query operation', async () => { + const yoga = createYoga({ + plugins: [ + useResponseCache({ + session: () => null, + includeExtensionMetadata: true, + }), + ], + }) + function fetch() { + return yoga.fetch('http://localhost:3000/graphql', { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify({ query: '{__typename}' }), + }) + } + + let response = await fetch() + + expect(response.status).toEqual(200) + let body = await response.json() + expect(body).toEqual({ + data: { + __typename: 'Query', + }, + extensions: { + responseCache: { + didCache: true, + hit: false, + ttl: null, + }, + }, + }) + + response = await fetch() + expect(response.status).toEqual(200) + body = await response.json() + expect(body).toEqual({ + data: { + __typename: 'Query', + }, + extensions: { + responseCache: { + hit: true, + }, + }, + }) +}) + +it('cache a query operation per session', async () => { + const yoga = createYoga({ + plugins: [ + useResponseCache({ + session: (_, request) => request.headers.get('x-session-id') ?? null, + includeExtensionMetadata: true, + }), + ], + }) + function fetch(sessionId: string) { + return yoga.fetch('http://localhost:3000/graphql', { + method: 'POST', + headers: { + 'content-type': 'application/json', + 'x-session-id': sessionId, + }, + body: JSON.stringify({ query: '{__typename}' }), + }) + } + + let response = await fetch('1') + + expect(response.status).toEqual(200) + let body = await response.json() + expect(body).toEqual({ + data: { + __typename: 'Query', + }, + extensions: { + responseCache: { + didCache: true, + hit: false, + ttl: null, + }, + }, + }) + + response = await fetch('1') + expect(response.status).toEqual(200) + body = await response.json() + expect(body).toEqual({ + data: { + __typename: 'Query', + }, + extensions: { + responseCache: { + hit: true, + }, + }, + }) + + response = await fetch('2') + + expect(response.status).toEqual(200) + body = await response.json() + expect(body).toEqual({ + data: { + __typename: 'Query', + }, + extensions: { + responseCache: { + didCache: true, + hit: false, + ttl: null, + }, + }, + }) +}) diff --git a/packages/plugins/response-cache/package.json b/packages/plugins/response-cache/package.json index e25df5e78d..e85673755a 100644 --- a/packages/plugins/response-cache/package.json +++ b/packages/plugins/response-cache/package.json @@ -49,8 +49,7 @@ "access": "public" }, "dependencies": { - "@envelop/response-cache": "2.4.0", - "tslib": "^2.3.1" + "@envelop/response-cache": "^3.0.0" }, "peerDependencies": { "graphql": "^15.2.0 || ^16.0.0", diff --git a/packages/plugins/response-cache/src/index.ts b/packages/plugins/response-cache/src/index.ts index 6300ea4632..6901a358ba 100644 --- a/packages/plugins/response-cache/src/index.ts +++ b/packages/plugins/response-cache/src/index.ts @@ -2,34 +2,34 @@ import { BuildResponseCacheKeyFunction, createInMemoryCache, defaultBuildResponseCacheKey, - GetDocumentStringFromContextFunction, useResponseCache as useEnvelopResponseCache, UseResponseCacheParameter as UseEnvelopResponseCacheParameter, } from '@envelop/response-cache' -import { GraphQLParams, Maybe, Plugin, YogaInitialContext } from 'graphql-yoga' +import { + GraphQLParams, + Maybe, + Plugin, + PromiseOrValue, + YogaInitialContext, +} from 'graphql-yoga' export type UseResponseCacheParameter = Omit< UseEnvelopResponseCacheParameter, - 'getDocumentStringFromContext' | 'session' + 'getDocumentString' | 'session' > & { - session: (params: GraphQLParams, request: Request) => Maybe + session: ( + params: GraphQLParams, + request: Request, + ) => PromiseOrValue> enabled?: (params: GraphQLParams, request: Request) => boolean } -// Probably this is not used but somehow if Envelop plugin needs that -const getDocumentStringFromContext: GetDocumentStringFromContextFunction = ( - context, -) => context.query as string - const operationIdByRequest = new WeakMap() // We trick Envelop plugin by passing operationId as sessionId so we can take it from cache key builder we pass to Envelop function sessionFactoryForEnvelop({ request }: YogaInitialContext) { return operationIdByRequest.get(request) } -const buildResponseCacheKeyForEnvelop: BuildResponseCacheKeyFunction = async ({ - sessionId, -}) => sessionId! export function useResponseCache(options: UseResponseCacheParameter): Plugin { const buildResponseCacheKey: BuildResponseCacheKeyFunction = @@ -42,9 +42,7 @@ export function useResponseCache(options: UseResponseCacheParameter): Plugin { useEnvelopResponseCache({ ...options, cache, - getDocumentStringFromContext, session: sessionFactoryForEnvelop, - buildResponseCacheKey: buildResponseCacheKeyForEnvelop, }), ) }, @@ -56,7 +54,7 @@ export function useResponseCache(options: UseResponseCacheParameter): Plugin { documentString: params.query!, variableValues: params.variables, operationName: params.operationName, - sessionId: options.session(params, request), + sessionId: await options.session(params, request), }) const cachedResponse = await cache.get(operationId) if (cachedResponse) {