Add React Apollo for the GraphQL client side

This commit is contained in:
genomics-geek 2019-10-05 11:53:04 -04:00
parent f01e3bfd16
commit 2952e46bb7
8 changed files with 183 additions and 3 deletions

View File

@ -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"
},

View File

@ -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

View File

@ -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

View File

@ -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')}`,
},
}
})

View File

@ -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 })

View File

@ -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
}

View File

@ -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
})
}

View File

@ -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 = () => (
<Router history={history}>
<Routes />
</Router>
<ApolloProvider client={client}>
<AlertProvider template={AlertTemplate}>
<Router history={history}>
<Routes />
</Router>
</AlertProvider>
</ApolloProvider>
)
export default Root