Skip to content

Instantly share code, notes, and snippets.

@emaraschio
Created March 3, 2020 21:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save emaraschio/8cf32f42e50a25882d6f1ec429e147e3 to your computer and use it in GitHub Desktop.
Save emaraschio/8cf32f42e50a25882d6f1ec429e147e3 to your computer and use it in GitHub Desktop.
import ApolloClient, { FetchPolicy } from "apollo-client"
import { HttpLink } from "apollo-link-http"
import { InMemoryCache, NormalizedCacheObject } from "apollo-cache-inmemory"
// import { setContext } from "apollo-link-context"
import ActionCable from "action-cable-react-jwt"
import ActionCableLink from "graphql-ruby-client/dist/subscriptions/ActionCableLink"
import { ApolloLink } from "apollo-link"
import { handleAuthentication, refreshToken } from "utils/oauth"
import { Observable } from "apollo-link"
import { onError } from "apollo-link-error"
import config from "config"
const promiseToObservable = (promise: Promise<any>) =>
new Observable((subscriber: any) => {
promise.then(
value => {
if (subscriber.closed) return
subscriber.next(value)
subscriber.complete()
},
error => subscriber.error(error)
)
})
const defaultOptions = {
watchQuery: {
fetchPolicy: "cache-and-network" as FetchPolicy
}
}
const getTokens = async () => {
const token = localStorage.getItem("token")
const freshToken = localStorage.getItem("refresh_token")
if (token && freshToken) {
await refreshToken()
} else {
await handleAuthentication()
}
const authorization = token ? `Bearer: ${token}` : ""
return token ? { authorization: authorization } : {}
}
const setTokenForOperation = async (operation: any) => {
return operation.setContext({
headers: {
// eslint-disable-next-line
...(await getTokens())
}
})
}
const hasSubscriptionOperation = ({ query: { definitions } }: any) => {
return definitions.some(({ kind, operation }: any) => {
return kind === "OperationDefinition" && operation === "subscription"
})
}
const createActionCableLink = () => {
const token = localStorage.getItem("token")
console.log('createActionCableLink: ', token)
const cable = ActionCable.createConsumer("ws://localhost:3004/cable", token)
return new ActionCableLink({ cable })
}
const createLinkWithToken = () =>
new ApolloLink(
(operation, forward) =>
new Observable(observer => {
let handle: any
Promise.resolve(operation)
.then(setTokenForOperation)
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer)
})
})
.catch(observer.error.bind(observer))
return () => {
if (handle) handle.unsubscribe()
}
})
)
const createErrorLink = () =>
onError(({ networkError, operation, forward }: any): any => {
if (networkError) {
switch (networkError.statusCode) {
case 401:
const token = localStorage.getItem("token")
const freshToken = localStorage.getItem("refresh_token")
if (token && freshToken) {
return promiseToObservable(refreshToken()).flatMap(() => forward(operation))
} else {
return promiseToObservable(handleAuthentication()).flatMap(() => forward(operation))
}
default:
}
}
})
const createHttpLink = (fetch = undefined, uri = config.api.baseUrl) =>
new HttpLink({
fetch,
uri
})
export const createClient = ({ fetch = undefined, uri = config.api.baseUrl }): ApolloClient<NormalizedCacheObject> => {
const cache = new InMemoryCache()
const client = new ApolloClient({
link: ApolloLink.from([
createErrorLink(),
createLinkWithToken(),
ApolloLink.split(hasSubscriptionOperation, createActionCableLink(), createHttpLink(fetch, uri)),
]) ,
cache,
defaultOptions
})
return client
}
export default createClient
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment