import { ApolloClient, ApolloLink, createHttpLink, from, InMemoryCache, split } from '@apollo/client/core' import { setContext } from '@apollo/client/link/context' import { WebSocketLink } from '@apollo/client/link/ws' import { getMainDefinition } from '@apollo/client/utilities' import type { GetTokenSilentlyOptions } from '@auth0/auth0-spa-js' import type { Auth0VueClient } from '@auth0/auth0-vue' import { SpanKind, TraceFlags } from '@opentelemetry/api' import { DefaultApolloClient, provideApolloClient } from '@vue/apollo-composable' import { defineNuxtPlugin, useNuxtApp } from '#app' import { envConfig } from '~/utils/environment' const apiUrl = envConfig(window.location.hostname).apiUrl const wsUrl = apiUrl.replace(/^http/, 'ws') const cache = new InMemoryCache({ typePolicies: { }, }) const getToken = async (options: GetTokenSilentlyOptions) => { const nuxtApp = useNuxtApp() const auth0: Auth0VueClient = nuxtApp.$auth0 as Auth0VueClient return await auth0.getAccessTokenSilently(options).catch(() => { return undefined }) } const httpLink = createHttpLink({ uri: apiUrl, }) const wsLink = new WebSocketLink({ uri: wsUrl, options: { reconnect: true, lazy: true, connectionParams: () => { return getToken({}).then((token) => ({ authToken: token, })) }, }, }) const authLink = setContext(async (_, { headers }) => { return await getToken({}).then((token) => ({ headers: { ...headers, authorization: token ? `Bearer ${token}` : '', }, })) }) const createSpanLink = new ApolloLink((operation, forward) => { const nuxtApp = useNuxtApp() if (nuxtApp.$faro) { const { trace } = nuxtApp.$faro.api.getOTEL() const span = trace.getTracer('default').startSpan(`gql.${operation.operationName}`, { kind: SpanKind.INTERNAL, // 0: Internal, 1: Server, 2: Client, 3: Producer, 4: Consumer }) const spanContext = span.spanContext() const traceParent = '00' + '-' + spanContext.traceId + '-' + spanContext.spanId + '-0' + Number(spanContext.traceFlags || TraceFlags.NONE).toString(16) operation.setContext({ span, headers: { ...operation.getContext().headers, 'traceparent': traceParent } }) return forward(operation).map((data) => { span.end() return data }) } return forward(operation) }) const link = from([ createSpanLink, split( ({ query }) => { const definition = getMainDefinition(query) return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ) }, authLink.concat(wsLink), authLink.concat(httpLink), ), ]) const instance = new ApolloClient({ connectToDevTools: true, link, cache, defaultOptions: { query: { fetchPolicy: 'cache-first', }, watchQuery: { fetchPolicy: 'cache-and-network', }, }, }) export default defineNuxtPlugin((nuxtApp) => { nuxtApp.provide(DefaultApolloClient[Symbol.toStringTag], instance) provideApolloClient(instance) })