hotfix: graphql connection error handler (#4523)
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
@@ -452,6 +452,7 @@
|
|||||||
},
|
},
|
||||||
"graphql": {
|
"graphql": {
|
||||||
"connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?",
|
"connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?",
|
||||||
|
"connection_error_http": "Failed to fetch GraphQL Schema due to network error.",
|
||||||
"connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is",
|
"connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is",
|
||||||
"connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is",
|
"connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is",
|
||||||
"mutations": "Mutations",
|
"mutations": "Mutations",
|
||||||
|
|||||||
@@ -224,7 +224,10 @@ watch(
|
|||||||
watch(
|
watch(
|
||||||
() => connection,
|
() => connection,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal.error && newVal.state === "DISCONNECTED") {
|
if (
|
||||||
|
newVal.error &&
|
||||||
|
(newVal.state === "DISCONNECTED" || newVal.state === "ERROR")
|
||||||
|
) {
|
||||||
const response = [
|
const response = [
|
||||||
{
|
{
|
||||||
type: "error",
|
type: "error",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
printSchema,
|
printSchema,
|
||||||
} from "graphql"
|
} from "graphql"
|
||||||
import { Component, computed, reactive, ref } from "vue"
|
import { Component, computed, reactive, ref } from "vue"
|
||||||
|
import { useToast } from "~/composables/toast"
|
||||||
import { getService } from "~/modules/dioc"
|
import { getService } from "~/modules/dioc"
|
||||||
import { getI18n } from "~/modules/i18n"
|
import { getI18n } from "~/modules/i18n"
|
||||||
|
|
||||||
@@ -52,7 +53,11 @@ export type GQLResponseEvent =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConnectionState = "CONNECTING" | "CONNECTED" | "DISCONNECTED"
|
export type ConnectionState =
|
||||||
|
| "CONNECTING"
|
||||||
|
| "CONNECTED"
|
||||||
|
| "DISCONNECTED"
|
||||||
|
| "ERROR"
|
||||||
export type SubscriptionState = "SUBSCRIBING" | "SUBSCRIBED" | "UNSUBSCRIBED"
|
export type SubscriptionState = "SUBSCRIBING" | "SUBSCRIBED" | "UNSUBSCRIBED"
|
||||||
|
|
||||||
const GQL = {
|
const GQL = {
|
||||||
@@ -100,10 +105,7 @@ export const gqlMessageEvent = ref<GQLResponseEvent | "reset">()
|
|||||||
|
|
||||||
export const schemaString = computed(() => {
|
export const schemaString = computed(() => {
|
||||||
if (!connection.schema) return ""
|
if (!connection.schema) return ""
|
||||||
|
return printSchema(connection.schema)
|
||||||
return printSchema(connection.schema, {
|
|
||||||
commentDescriptions: true,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const queryFields = computed(() => {
|
export const queryFields = computed(() => {
|
||||||
@@ -159,21 +161,40 @@ export const graphqlTypes = computed(() => {
|
|||||||
|
|
||||||
let timeoutSubscription: any
|
let timeoutSubscription: any
|
||||||
|
|
||||||
export const connect = async (url: string, headers: GQLHeader[]) => {
|
export const connect = async (
|
||||||
|
url: string,
|
||||||
|
headers: GQLHeader[],
|
||||||
|
isRunGQLOperation = false
|
||||||
|
) => {
|
||||||
if (connection.state === "CONNECTED") {
|
if (connection.state === "CONNECTED") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"A connection is already running. Close it before starting another."
|
"A connection is already running. Close it before starting another."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Polling
|
const toast = useToast()
|
||||||
connection.state = "CONNECTED"
|
const t = getI18n()
|
||||||
|
|
||||||
|
connection.state = "CONNECTING"
|
||||||
|
|
||||||
const poll = async () => {
|
const poll = async () => {
|
||||||
await getSchema(url, headers)
|
try {
|
||||||
timeoutSubscription = setTimeout(() => {
|
await getSchema(url, headers)
|
||||||
poll()
|
// polling for schema
|
||||||
}, GQL_SCHEMA_POLL_INTERVAL)
|
if (connection.state !== "CONNECTED") connection.state = "CONNECTED"
|
||||||
|
timeoutSubscription = setTimeout(() => {
|
||||||
|
poll()
|
||||||
|
}, GQL_SCHEMA_POLL_INTERVAL)
|
||||||
|
} catch (error) {
|
||||||
|
connection.state = "ERROR"
|
||||||
|
|
||||||
|
// Show an error toast if the introspection attempt failed and not while sending a request
|
||||||
|
if (!isRunGQLOperation) {
|
||||||
|
toast.error(t("graphql.connection_error_http"))
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await poll()
|
await poll()
|
||||||
@@ -221,6 +242,8 @@ const getSchema = async (url: string, headers: GQLHeader[]) => {
|
|||||||
const res = await interceptorService.runRequest(reqOptions).response
|
const res = await interceptorService.runRequest(reqOptions).response
|
||||||
|
|
||||||
if (E.isLeft(res)) {
|
if (E.isLeft(res)) {
|
||||||
|
connection.state = "ERROR"
|
||||||
|
|
||||||
if (
|
if (
|
||||||
res.left !== "cancellation" &&
|
res.left !== "cancellation" &&
|
||||||
res.left.error === "NO_PW_EXT_HOOK" &&
|
res.left.error === "NO_PW_EXT_HOOK" &&
|
||||||
@@ -237,6 +260,17 @@ const getSchema = async (url: string, headers: GQLHeader[]) => {
|
|||||||
throw new Error(res.left.toString())
|
throw new Error(res.left.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (res.right.status !== 200) {
|
||||||
|
connection.state = "ERROR"
|
||||||
|
connection.error = {
|
||||||
|
type: "HTTP_ERROR",
|
||||||
|
message: (t: ReturnType<typeof getI18n>) =>
|
||||||
|
t("graphql.connection_error_http"),
|
||||||
|
component: undefined,
|
||||||
|
}
|
||||||
|
throw new Error("Failed to fetch schema. Status: " + res.right.status)
|
||||||
|
}
|
||||||
|
|
||||||
const data = res.right
|
const data = res.right
|
||||||
|
|
||||||
// HACK : Temporary trailing null character issue from the extension fix
|
// HACK : Temporary trailing null character issue from the extension fix
|
||||||
@@ -258,7 +292,7 @@ const getSchema = async (url: string, headers: GQLHeader[]) => {
|
|||||||
|
|
||||||
export const runGQLOperation = async (options: RunQueryOptions) => {
|
export const runGQLOperation = async (options: RunQueryOptions) => {
|
||||||
if (connection.state !== "CONNECTED") {
|
if (connection.state !== "CONNECTED") {
|
||||||
await connect(options.url, options.headers)
|
await connect(options.url, options.headers, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { url, headers, query, variables, auth, operationName, operationType } =
|
const { url, headers, query, variables, auth, operationName, operationType } =
|
||||||
|
|||||||
@@ -87,7 +87,8 @@
|
|||||||
import { usePageHead } from "@composables/head"
|
import { usePageHead } from "@composables/head"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import { computed, onBeforeUnmount, ref } from "vue"
|
import { computed, onBeforeUnmount, ref, watch } from "vue"
|
||||||
|
import { useToast } from "~/composables/toast"
|
||||||
import { defineActionHandler } from "~/helpers/actions"
|
import { defineActionHandler } from "~/helpers/actions"
|
||||||
import { connection, disconnect } from "~/helpers/graphql/connection"
|
import { connection, disconnect } from "~/helpers/graphql/connection"
|
||||||
import { getDefaultGQLRequest } from "~/helpers/graphql/default"
|
import { getDefaultGQLRequest } from "~/helpers/graphql/default"
|
||||||
@@ -97,6 +98,7 @@ import { HoppTab } from "~/services/tab"
|
|||||||
import { GQLTabService } from "~/services/tab/graphql"
|
import { GQLTabService } from "~/services/tab/graphql"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
const toast = useToast()
|
||||||
const tabs = useService(GQLTabService)
|
const tabs = useService(GQLTabService)
|
||||||
|
|
||||||
const currentTabID = computed(() => tabs.currentTabID.value)
|
const currentTabID = computed(() => tabs.currentTabID.value)
|
||||||
|
|||||||
Reference in New Issue
Block a user