From 0ea68438323aa3894a5e8b500562988cec514af9 Mon Sep 17 00:00:00 2001 From: DavidC Date: Wed, 29 Nov 2017 18:48:24 +0100 Subject: [PATCH] feat(contextware for dataloaders): added kind of middleware that is run on each operation to initial --- README.md | 14 ++++++++++---- src/bridgeLink.js | 18 ++++++++++++++++-- src/bridgeLink.test.js | 19 ++++++++++++++++++- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ef5b1a9..400d636 100644 --- a/README.md +++ b/README.md @@ -53,18 +53,24 @@ import { InMemoryCache } from 'apollo-cache-inmemory'; import schema from './schema'; // your resolvers (see example) -import reolvers from './resolvers'; +import resolvers from './resolvers'; // if you want to mock anything in the schema (that has not resolver or doesn't return data) const mock = true; // if you want to push something down to the resolvers in context const context = { - graphQl: 'is cool' -} + graphQl: 'is cool', +}; + +// do something with actual context and operation of gql operation +// and return object that will be shallow merged to context +// use for init dataloader with token for example +// see example... https://github.com/dacz/apollo-bridge-link-example +const contextware = (ctx, operation) => ({ inContext: 'from contextware' }); export const client = new ApolloClient({ - link: BridgeLink({ schema, resolvers, mock, context }); + link: BridgeLink({ schema, resolvers, mock, context, contextware }); cache: new InMemoryCache({ addTypename: true }), }); ``` diff --git a/src/bridgeLink.js b/src/bridgeLink.js index e2f2eb0..737fb23 100644 --- a/src/bridgeLink.js +++ b/src/bridgeLink.js @@ -4,7 +4,13 @@ import { addMockFunctionsToSchema, makeExecutableSchema } from 'graphql-tools'; import { ApolloLink } from 'apollo-link'; import Observable from 'zen-observable'; -export const createBridgeLink = ({ schema, resolvers, mock, context = {} }) => { +export const createBridgeLink = ({ + schema, + resolvers, + mock, + context = {}, + contextware = [], +}) => { let executableSchema; if (typeof schema === 'string') { executableSchema = makeExecutableSchema({ typeDefs: schema, resolvers }); @@ -31,12 +37,20 @@ export const createBridgeLink = ({ schema, resolvers, mock, context = {} }) => { operation => new Observable(observer => { const { headers, credentials } = operation.getContext(); - const ctx = { + const ctxInitial = { ...context, headers, credentials, }; + // contextware + const ctx = [].concat(contextware).reduce((acc, fn) => { + const rv = fn(acc, operation); + return typeof rv === 'object' && !Array.isArray(rv) + ? { ...acc, ...rv } + : acc; + }, ctxInitial); + graphql( executableSchema, print(operation.query), diff --git a/src/bridgeLink.test.js b/src/bridgeLink.test.js index be571fa..3cc02c1 100644 --- a/src/bridgeLink.test.js +++ b/src/bridgeLink.test.js @@ -86,6 +86,23 @@ test('BridgeLink mocks data', async t => { t.truthy(res.data.posts[0].author.id, 'should have mocked author'); }); +test('BridgeLink calls contextware', async t => { + const returning = { middle: 'data' }; + const init = sinon.mock().returns(returning); + const opts = { + schema: schemaExample, + mock: true, + contextware: init, + }; + const link = new BridgeLink(opts); + const cache = new InMemoryCache(); + const client = new ApolloClient({ link, cache }); + const res = await client.query({ query: POSTS }); + t.true(res.data.posts.length > 0, 'should get some mocked data'); + t.truthy(res.data.posts[0].author.id, 'should have mocked author'); + t.true(init.calledOnce); +}); + test('BridgeLink should accept executable schema', async t => { const posts = [ { @@ -170,7 +187,7 @@ test('createBridgeLink - creates link', t => { const schema = schemaExample; const resolvers = {}; const mock = false; - const context = {}; + const context = { gq: 'is cool' }; const link = createBridgeLink({ schema, resolvers, mock, context }); t.true(link instanceof ApolloLink); });