import {InMemoryCache, defaultDataIdFromObject} from 'apollo-cache-inmemory/lib/index' import {createHttpLink} from 'apollo-link-http' import {onError} from 'apollo-link-error'; import {ApolloClient} from 'apollo-client' import {ApolloLink, Observable} from 'apollo-link' import fetch from 'unfetch' import {typeDefs} from '@/graphql/typedefs'; import {resolvers} from '@/graphql/resolvers'; const writeLocalCache = cache => { // we use the cache as our local state cache.writeData({ data: { scrollPosition: { __typename: 'ScrollPosition', scrollTo: '' }, sidebar: { __typename: 'Sidebar', open: false }, helloEmail: { __typename: 'HelloEmail', email: '' }, } }); }; export default function (uri, networkErrorCallback) { const httpLink = createHttpLink({ // uri: process.env.NODE_ENV !== 'production' ? 'http://localhost:8000/api/graphql/' : '/api/graphql/', uri, credentials: 'include', fetch: fetch, headers: { 'X-CSRFToken': document.cookie.replace(/(?:(?:^|.*;\s*)csrftoken\s*=\s*([^;]*).*$)|^.*$/, '$1') } }); const consoleLink = new ApolloLink((operation, forward) => { // console.log(`starting request for ${operation.operationName}`); return forward(operation).map((data) => { // console.log(`ending request for ${operation.operationName}`); return data }) }); // from https://github.com/apollographql/apollo-client/issues/1564#issuecomment-357492659 const omitTypename = (key, value) => { return key === '__typename' ? undefined : value }; const createOmitTypenameLink = new ApolloLink((operation, forward) => { if (operation.variables) { operation.variables = JSON.parse(JSON.stringify(operation.variables), omitTypename) } return forward(operation) }); const errorLink = onError(({response, operation, networkError, graphQLErrors}) => { if (networkError && networkErrorCallback) { networkErrorCallback(networkError.statusCode); return Observable.of(); } if (graphQLErrors) { graphQLErrors.forEach(({message, locations, path}) => console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ) ); } /* The server redirects to the HTML login page, but apollo expects a JSON response. This is fine, we'll just ignore it */ if (networkError && networkError.name === 'ServerParseError') { // workaround found here: https://github.com/apollographql/apollo-link/issues/855#issuecomment-485764697 return Observable.of(); } }); const composedLink = ApolloLink.from([createOmitTypenameLink, consoleLink, errorLink, httpLink]); const cache = new InMemoryCache({ dataIdFromObject: obj => { switch (obj.__typename) { case 'InstrumentNode': case 'ModuleNode': return `${obj.__typename}:${obj.slug}`; default: return defaultDataIdFromObject(obj); } }, cacheRedirects: { Query: { contentBlock: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ContentBlockNode', id: args.id}), chapter: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ChapterNode', id: args.id}), assignment: (_, args, {getCacheKey}) => getCacheKey({__typename: 'AssignmentNode', id: args.id}), objective: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveNode', id: args.id}), objectiveGroup: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveGroupNode', id: args.id}), projectEntry: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ProjectEntryNode', id: args.id}), } } }); // TODO: Monkey-patching in a fix for an open issue suggesting that // `readQuery` should return null or undefined if the query is not yet in the // cache: https://github.com/apollographql/apollo-feature-requests/issues/1 cache.originalReadQuery = cache.readQuery; cache.readQuery = (...args) => { try { return cache.originalReadQuery(...args); } catch (err) { console.error(err); return undefined; } }; writeLocalCache(cache); // Create the apollo client const client = new ApolloClient({ link: composedLink, // link: httpLink, cache, connectToDevTools: true, typeDefs, resolvers }); client.onResetStore(() => { writeLocalCache(cache); }); return client; }