This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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