diff --git a/{{cookiecutter.project_slug}}/frontend/package.json b/{{cookiecutter.project_slug}}/frontend/package.json
index 5c19366c7..3a3ceeecb 100644
--- a/{{cookiecutter.project_slug}}/frontend/package.json
+++ b/{{cookiecutter.project_slug}}/frontend/package.json
@@ -18,9 +18,22 @@
"react-scripts": "3.1.1"
},
"devDependencies": {
+ "@apollo/react-common": "3.1.2",
+ "@apollo/react-components": "3.1.2",
+ "@apollo/react-hooks": "3.1.2",
+ "@apollo/react-testing": "3.1.2",
+ "apollo-boost": "0.4.4",
+ "apollo-cache-persist": "0.1.1",
+ "apollo-link-context": "1.0.19",
+ "apollo-link-rest": "0.7.3",
+ "apollo-link-schema": "1.2.4",
+ "apollo-link-state": "0.4.2",
+ "apollo-upload-client": "11.0.0",
"axios": "0.19.0",
"node-sass": "4.12.0",
"raven-js": "3.27.2",
+ "react-alert": "5.5.0",
+ "react-alert-template-basic": "1.0.0",
"react-document-title": "2.0.3",
"react-router-dom": "5.1.2"
},
diff --git a/{{cookiecutter.project_slug}}/frontend/src/apollo/cache.js b/{{cookiecutter.project_slug}}/frontend/src/apollo/cache.js
new file mode 100644
index 000000000..ae62da7cc
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/frontend/src/apollo/cache.js
@@ -0,0 +1,9 @@
+import { InMemoryCache } from 'apollo-cache-inmemory'
+import { persistCache } from 'apollo-cache-persist'
+import localForage from 'localforage'
+
+const cache = new InMemoryCache()
+
+persistCache({cache, storage: localForage})
+
+export default cache
diff --git a/{{cookiecutter.project_slug}}/frontend/src/apollo/client.js b/{{cookiecutter.project_slug}}/frontend/src/apollo/client.js
new file mode 100644
index 000000000..ab83ff397
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/frontend/src/apollo/client.js
@@ -0,0 +1,36 @@
+import { ApolloClient } from 'apollo-client'
+import { ApolloLink } from 'apollo-link'
+
+import { authLink, restLink, stateLink, uploadLink } from './links'
+import cache from './cache'
+import { mockLink } from './mocks'
+
+const defaultOptions = {
+ watchQuery: {
+ fetchPolicy: 'cache-and-network',
+ errorPolicy: 'ignore',
+ },
+ query: {
+ fetchPolicy: 'cache-and-network',
+ errorPolicy: 'all',
+ },
+ mutate: {
+ // NOTE: Using 'none' will allow Apollo to recognize errors even if the response
+ // includes {data: null} in it (graphene does this with each unsuccessful mutation!)
+ errorPolicy: 'none',
+ },
+}
+
+const link = ApolloLink.from([stateLink, restLink, authLink.concat(uploadLink)])
+
+const client = new ApolloClient({
+ link:
+ process.env.NODE_ENV === 'production'
+ ? link // never use mock for production
+ : ApolloLink.split(operation => operation.getContext().mock, mockLink, link),
+ cache,
+ connectToDevTools: process.env.NODE_ENV === 'production' ? false : true,
+ defaultOptions,
+})
+
+export default client
diff --git a/{{cookiecutter.project_slug}}/frontend/src/apollo/links.js b/{{cookiecutter.project_slug}}/frontend/src/apollo/links.js
new file mode 100644
index 000000000..ae0ba77bd
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/frontend/src/apollo/links.js
@@ -0,0 +1,43 @@
+import cookie from 'react-cookies'
+import { setContext } from 'apollo-link-context'
+import { createHttpLink } from 'apollo-link-http'
+import { RestLink } from 'apollo-link-rest'
+import { withClientState } from 'apollo-link-state'
+import { createUploadLink } from 'apollo-upload-client'
+import { merge } from 'lodash'
+
+import cache from './cache'
+
+// docs: https://www.apollographql.com/docs/link/links/http.html
+export const httpLink = createHttpLink({
+ uri: '/graphql/',
+ credentials: 'same-origin',
+})
+
+// docs: https://github.com/jaydenseric/apollo-upload-client
+export const uploadLink = createUploadLink({
+ uri: '/graphql/',
+ credentials: 'same-origin',
+})
+
+// docs: https://www.apollographql.com/docs/link/links/rest.html
+export const restLink = new RestLink({
+ uri: '/api/',
+ endpoints: {},
+})
+
+// docs: https://www.apollographql.com/docs/link/links/state.html
+export const stateLink = withClientState({
+ cache,
+ ...merge({}),
+})
+
+export const authLink = setContext((_, { headers }) => {
+ return {
+ headers: {
+ ...headers,
+ 'X-CSRFToken': cookie.load('csrftoken'),
+ Authorization: `JWT ${cookie.load('jwt')}`,
+ },
+ }
+})
diff --git a/{{cookiecutter.project_slug}}/frontend/src/apollo/mocks/index.js b/{{cookiecutter.project_slug}}/frontend/src/apollo/mocks/index.js
new file mode 100644
index 000000000..d845518ef
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/frontend/src/apollo/mocks/index.js
@@ -0,0 +1,19 @@
+import { SchemaLink } from 'apollo-link-schema'
+import { addMockFunctionsToSchema } from 'graphql-tools'
+import { assign } from 'lodash'
+
+import { mockSchema } from '../schema-parser'
+
+export const baseMocks = {}
+
+// Use this devMocks object for your development mock override
+// Make sure to empty this part before your pull request
+const devMocks = {}
+
+const combinedMocks = assign({}, baseMocks, devMocks)
+
+const schema = mockSchema()
+
+addMockFunctionsToSchema({ schema, mocks: combinedMocks })
+
+export const mockLink = new SchemaLink({ schema })
diff --git a/{{cookiecutter.project_slug}}/frontend/src/apollo/schema-parser.js b/{{cookiecutter.project_slug}}/frontend/src/apollo/schema-parser.js
new file mode 100644
index 000000000..39c32f507
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/frontend/src/apollo/schema-parser.js
@@ -0,0 +1,24 @@
+import { findIndex, remove } from 'lodash'
+import { buildClientSchema } from 'graphql'
+
+import * as jsonFile from './schema'
+
+export const mockSchema = () => {
+ // new json loader update adds a `defulat` parent level to json
+ const x = jsonFile.default
+
+ // Apollo won't accept introspection that has any '__debug' values in json file, so the parser should remove them first
+ // To read more: https://github.research.chop.edu/DGD/nexus/issues/1318#issuecomment-18281
+
+ const queryTypeIndex = findIndex(x.data.__schema.types, ['name', 'Query'])
+ const mutationTypeIndex = findIndex(x.data.__schema.types, ['name', 'Mutation'])
+
+ // remove the fields:
+
+ remove(x.data.__schema.types[queryTypeIndex].fields, field => field.name === '__debug')
+ remove(x.data.__schema.types[mutationTypeIndex].fields, field => field.name === '__debug')
+
+ const schema = buildClientSchema(x.data)
+
+ return schema
+}
diff --git a/{{cookiecutter.project_slug}}/frontend/src/apollo/test-helpers.js b/{{cookiecutter.project_slug}}/frontend/src/apollo/test-helpers.js
new file mode 100644
index 000000000..83a454566
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/frontend/src/apollo/test-helpers.js
@@ -0,0 +1,28 @@
+import { assign } from 'lodash'
+import { baseMocks } from './mocks'
+import { graphql } from 'graphql'
+import { addMockFunctionsToSchema } from 'graphql-tools'
+import { mockSchema } from './schema-parser'
+import { print as gqlToString } from 'graphql/language'
+
+
+export const mockQuery = ({query, mocks, variables = { id: 'id' }, log = false}) => {
+ // Arguments:
+ // (required) QUERY
+ // (optional) Mock object : to override base mocks
+ // (optional) Variables : If not passed, it will use a fake id only (most frequently used)
+ // (optional) Log : In case you want the query result to be shown with test results.
+
+ /// MOCKING SCHEMA:
+ const schema = mockSchema()
+ const combinedMocks = mocks ? assign({}, baseMocks, mocks) : baseMocks
+ addMockFunctionsToSchema({ schema, mocks: combinedMocks })
+
+ // need the first 'return' so that the final output is the promise result
+ return graphql(schema, gqlToString(query), null, null, variables).then(result => {
+ if (log) console.log('mockQuery result', result)
+ const { data, errors } = result
+ expect(errors).toBe()
+ return data
+ })
+}
diff --git a/{{cookiecutter.project_slug}}/frontend/src/root.js b/{{cookiecutter.project_slug}}/frontend/src/root.js
index b19501a51..5f4d7f65e 100644
--- a/{{cookiecutter.project_slug}}/frontend/src/root.js
+++ b/{{cookiecutter.project_slug}}/frontend/src/root.js
@@ -1,8 +1,12 @@
import React from 'react'
+import { Provider as AlertProvider } from 'react-alert'
+import AlertTemplate from 'react-alert-template-basic'
import { Router } from 'react-router-dom'
+import { ApolloProvider } from '@apollo/react-hooks'
import { createBrowserHistory as createHistory } from 'history'
import Raven from 'raven-js'
+import client from 'apollo/client'
import Routes from './routes'
if (process.env.NODE_ENV === 'production') {
@@ -12,9 +16,13 @@ if (process.env.NODE_ENV === 'production') {
const history = createHistory()
const Root = () => (
-
-
-
+
+
+
+
+
+
+
)
export default Root