feat: add subscription support for useGQLQuery

This commit is contained in:
Andrew Bastin
2021-10-27 22:41:32 +05:30
parent 4b0d7a6c3d
commit ddd29374ea
4 changed files with 85 additions and 33 deletions

View File

@@ -18,11 +18,13 @@ import {
makeOperation, makeOperation,
GraphQLRequest, GraphQLRequest,
createRequest, createRequest,
subscriptionExchange,
} from "@urql/core" } from "@urql/core"
import { authExchange } from "@urql/exchange-auth" import { authExchange } from "@urql/exchange-auth"
import { offlineExchange } from "@urql/exchange-graphcache" import { offlineExchange } from "@urql/exchange-graphcache"
import { makeDefaultStorage } from "@urql/exchange-graphcache/default-storage" import { makeDefaultStorage } from "@urql/exchange-graphcache/default-storage"
import { devtoolsExchange } from "@urql/devtools" import { devtoolsExchange } from "@urql/devtools"
import { SubscriptionClient } from "subscriptions-transport-ws"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import * as TE from "fp-ts/TaskEither" import * as TE from "fp-ts/TaskEither"
import { pipe, constVoid } from "fp-ts/function" import { pipe, constVoid } from "fp-ts/function"
@@ -33,13 +35,14 @@ import { updatesDef } from "./caching/updates"
import { resolversDef } from "./caching/resolvers" import { resolversDef } from "./caching/resolvers"
import schema from "./backend-schema.json" import schema from "./backend-schema.json"
import { import {
authIdToken$,
getAuthIDToken, getAuthIDToken,
probableUser$, probableUser$,
waitProbableLoginToConfirm, waitProbableLoginToConfirm,
} from "~/helpers/fb/auth" } from "~/helpers/fb/auth"
const BACKEND_GQL_URL = const BACKEND_GQL_URL =
process.env.CONTEXT === "production" process.env.context === "production"
? "https://api.hoppscotch.io/graphql" ? "https://api.hoppscotch.io/graphql"
: "https://api.hoppscotch.io/graphql" : "https://api.hoppscotch.io/graphql"
@@ -48,6 +51,18 @@ const storage = makeDefaultStorage({
maxAge: 7, maxAge: 7,
}) })
const subscriptionClient = new SubscriptionClient(
process.env.context === "production"
? "wss://api.hoppscotch.io/graphql"
: "wss://api.hoppscotch.io/graphql",
{
reconnect: true,
connectionParams: () => ({
authorization: `Bearer ${authIdToken$.value}`,
}),
}
)
export const client = createClient({ export const client = createClient({
url: BACKEND_GQL_URL, url: BACKEND_GQL_URL,
exchanges: [ exchanges: [
@@ -97,6 +112,9 @@ export const client = createClient({
}, },
}), }),
fetchExchange, fetchExchange,
subscriptionExchange({
forwardSubscription: (operation) => subscriptionClient.request(operation),
}),
], ],
}) })
@@ -106,6 +124,7 @@ type UseQueryOptions<T = any, V = object> = {
query: TypedDocumentNode<T, V> query: TypedDocumentNode<T, V>
variables?: MaybeRef<V> variables?: MaybeRef<V>
updateSubs?: MaybeRef<GraphQLRequest<any, object>[]>
defer?: boolean defer?: boolean
} }
@@ -133,6 +152,8 @@ export const useGQLQuery = <DocType, DocVarType, DocErrorType extends string>(
const isStale: Ref<boolean> = ref(true) const isStale: Ref<boolean> = ref(true)
const data: Ref<E.Either<GQLError<DocErrorType>, DocType>> = ref() as any const data: Ref<E.Either<GQLError<DocErrorType>, DocType>> = ref() as any
if (!args.updateSubs) set(args, "updateSubs", [])
const isPaused: Ref<boolean> = ref(args.defer ?? false) const isPaused: Ref<boolean> = ref(args.defer ?? false)
const request: Ref<GraphQLRequest<DocType, DocVarType>> = ref( const request: Ref<GraphQLRequest<DocType, DocVarType>> = ref(
@@ -178,7 +199,22 @@ export const useGQLQuery = <DocType, DocVarType, DocErrorType extends string>(
loading.value = true loading.value = true
isStale.value = false isStale.value = false
onInvalidate( const invalidateStops = args.updateSubs!.map((sub) => {
console.log("create sub")
return wonkaPipe(
client.executeSubscription(sub),
onEnd(() => {
if (source.value) execute()
}),
subscribe(() => {
console.log("invalidate!")
return execute()
})
).unsubscribe
})
invalidateStops.push(
wonkaPipe( wonkaPipe(
source.value, source.value,
onEnd(() => { onEnd(() => {
@@ -220,6 +256,8 @@ export const useGQLQuery = <DocType, DocVarType, DocErrorType extends string>(
}) })
).unsubscribe ).unsubscribe
) )
onInvalidate(() => invalidateStops.forEach((unsub) => unsub()))
} }
}) })

View File

@@ -1,6 +1,44 @@
import { GraphCacheUpdaters, MyTeamsDocument } from "../graphql" import { GraphCacheUpdaters, MyTeamsDocument } from "../graphql"
export const updatesDef: GraphCacheUpdaters = { export const updatesDef: GraphCacheUpdaters = {
Subscription: {
teamMemberAdded: (_r, { teamID }, cache, _info) => {
debugger
cache.invalidate(
{
__typename: "Team",
id: teamID,
},
"teamMembers"
)
},
teamMemberUpdated: (_r, { teamID }, cache, _info) => {
cache.invalidate(
{
__typename: "Team",
id: teamID,
},
"teamMembers"
)
cache.invalidate(
{
__typename: "Team",
id: teamID,
},
"myRole"
)
},
teamMemberRemoved: (_r, { teamID }, cache, _info) => {
cache.invalidate(
{
__typename: "Team",
id: teamID,
},
"teamMembers"
)
},
},
Mutation: { Mutation: {
deleteTeam: (_r, { teamID }, cache, _info) => { deleteTeam: (_r, { teamID }, cache, _info) => {
cache.updateQuery( cache.updateQuery(

View File

@@ -70,6 +70,7 @@
"socket.io-client": "^4.2.0", "socket.io-client": "^4.2.0",
"socketio-wildcard": "^2.0.0", "socketio-wildcard": "^2.0.0",
"splitpanes": "^2.3.8", "splitpanes": "^2.3.8",
"subscriptions-transport-ws": "^0.9.19",
"tern": "^0.24.3", "tern": "^0.24.3",
"vue-apollo": "^3.0.8", "vue-apollo": "^3.0.8",
"vue-cli-plugin-apollo": "^0.22.2", "vue-cli-plugin-apollo": "^0.22.2",

37
pnpm-lock.yaml generated
View File

@@ -105,6 +105,7 @@ importers:
stylelint: ^13.13.1 stylelint: ^13.13.1
stylelint-config-prettier: ^8.0.2 stylelint-config-prettier: ^8.0.2
stylelint-config-standard: ^22.0.0 stylelint-config-standard: ^22.0.0
subscriptions-transport-ws: ^0.9.19
tern: ^0.24.3 tern: ^0.24.3
ts-jest: ^27.0.5 ts-jest: ^27.0.5
typescript: ^4.4.3 typescript: ^4.4.3
@@ -121,7 +122,7 @@ importers:
worker-loader: ^3.0.8 worker-loader: ^3.0.8
yargs-parser: ^20.2.9 yargs-parser: ^20.2.9
dependencies: dependencies:
'@apollo/client': 3.4.16_graphql@15.6.1 '@apollo/client': 3.4.16_9725b5bd6f8ccdf8972896e366c3daff
'@hoppscotch/js-sandbox': link:../hoppscotch-js-sandbox '@hoppscotch/js-sandbox': link:../hoppscotch-js-sandbox
'@nuxtjs/axios': 5.13.6 '@nuxtjs/axios': 5.13.6
'@nuxtjs/composition-api': 0.29.2_nuxt@2.15.8 '@nuxtjs/composition-api': 0.29.2_nuxt@2.15.8
@@ -158,6 +159,7 @@ importers:
socket.io-client: 4.2.0 socket.io-client: 4.2.0
socketio-wildcard: 2.0.0 socketio-wildcard: 2.0.0
splitpanes: 2.3.8 splitpanes: 2.3.8
subscriptions-transport-ws: 0.9.19_graphql@15.6.1
tern: 0.24.3 tern: 0.24.3
vue-apollo: 3.0.8_graphql-tag@2.12.5 vue-apollo: 3.0.8_graphql-tag@2.12.5
vue-cli-plugin-apollo: 0.22.2_typescript@4.4.3 vue-cli-plugin-apollo: 0.22.2_typescript@4.4.3
@@ -310,33 +312,6 @@ packages:
zen-observable-ts: 1.1.0 zen-observable-ts: 1.1.0
dev: false dev: false
/@apollo/client/3.4.16_graphql@15.6.1:
resolution: {integrity: sha512-iF4zEYwvebkri0BZQyv8zfavPfVEafsK0wkOofa6eC2yZu50J18uTutKtC174rjHZ2eyxZ8tV7NvAPKRT+OtZw==}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0
react: ^16.8.0 || ^17.0.0
subscriptions-transport-ws: ^0.9.0
peerDependenciesMeta:
react:
optional: true
subscriptions-transport-ws:
optional: true
dependencies:
'@graphql-typed-document-node/core': 3.1.0_graphql@15.6.1
'@wry/context': 0.6.1
'@wry/equality': 0.5.2
'@wry/trie': 0.3.1
graphql: 15.6.1
graphql-tag: 2.12.5_graphql@15.6.1
hoist-non-react-statics: 3.3.2
optimism: 0.16.1
prop-types: 15.7.2
symbol-observable: 4.0.0
ts-invariant: 0.9.3
tslib: 2.3.1
zen-observable-ts: 1.1.0
dev: false
/@apollo/federation/0.27.0_graphql@15.6.1: /@apollo/federation/0.27.0_graphql@15.6.1:
resolution: {integrity: sha512-hMeRN9IPsIn+5J5SmWof0ODbvRjRj8mBNqbsm9Zjkqjbw6RTlcx90taMk7cYhcd/E+uTyLQt5cOSRVBx53cxbQ==} resolution: {integrity: sha512-hMeRN9IPsIn+5J5SmWof0ODbvRjRj8mBNqbsm9Zjkqjbw6RTlcx90taMk7cYhcd/E+uTyLQt5cOSRVBx53cxbQ==}
engines: {node: '>=12.13.0 <17.0'} engines: {node: '>=12.13.0 <17.0'}
@@ -3058,7 +3033,7 @@ packages:
form-data: 4.0.0 form-data: 4.0.0
graphql: 15.6.1 graphql: 15.6.1
graphql-sse: 1.0.4_graphql@15.6.1 graphql-sse: 1.0.4_graphql@15.6.1
graphql-ws: 5.5.0_graphql@15.6.1 graphql-ws: 5.5.3_graphql@15.6.1
is-promise: 4.0.0 is-promise: 4.0.0
isomorphic-ws: 4.0.1_ws@8.2.2 isomorphic-ws: 4.0.1_ws@8.2.2
lodash: 4.17.21 lodash: 4.17.21
@@ -10432,8 +10407,8 @@ packages:
graphql: 15.6.1 graphql: 15.6.1
dev: false dev: false
/graphql-ws/5.5.0_graphql@15.6.1: /graphql-ws/5.5.3_graphql@15.6.1:
resolution: {integrity: sha512-WQepPMGQQoqS2VsrI2I3RMLCVz3CW4/6ZqGV6ABDOwH4R62DzjxwMlwZbj6vhSI/7IM3/C911yITwgs77iO/hw==} resolution: {integrity: sha512-Okp3gE3vq9OoeqsYVbmzKvPcvlinKNXrfVajH7D3ul1UdCg2+K2zVYbWKmqxehkAZ+GKVfngK5fzyXSsfpe+pA==}
engines: {node: '>=10'} engines: {node: '>=10'}
peerDependencies: peerDependencies:
graphql: '>=0.11 <=16' graphql: '>=0.11 <=16'