From 780dd8a7137934e58b2ae01181ce996bd512f0dd Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Fri, 23 Jun 2023 00:02:23 +0600 Subject: [PATCH] fix: graphql authorization headers (#3136) --- .../src/components/graphql/Authorization.vue | 26 +++++++++-- .../src/components/graphql/Request.vue | 15 ++++++- .../src/components/smart/EnvInput.vue | 4 +- .../src/helpers/GQLConnection.ts | 44 ++++++++++++++++--- .../src/newstore/GQLSession.ts | 5 +-- .../src/newstore/localpersistence.ts | 25 +++++++++++ .../hoppscotch-common/src/pages/graphql.vue | 12 +---- 7 files changed, 104 insertions(+), 27 deletions(-) diff --git a/packages/hoppscotch-common/src/components/graphql/Authorization.vue b/packages/hoppscotch-common/src/components/graphql/Authorization.vue index b9a6a58fa..2006470a3 100644 --- a/packages/hoppscotch-common/src/components/graphql/Authorization.vue +++ b/packages/hoppscotch-common/src/components/graphql/Authorization.vue @@ -135,12 +135,14 @@
@@ -148,21 +150,37 @@
- +
- +
- +
- +
diff --git a/packages/hoppscotch-common/src/components/graphql/Request.vue b/packages/hoppscotch-common/src/components/graphql/Request.vue index 08a985790..2c8aa4d4e 100644 --- a/packages/hoppscotch-common/src/components/graphql/Request.vue +++ b/packages/hoppscotch-common/src/components/graphql/Request.vue @@ -17,6 +17,7 @@ () const connected = useReadonlyStream(props.conn.connected$, false) +const isLoading = useReadonlyStream(props.conn.isLoading$, false) const headers = useReadonlyStream(gqlHeaders$, []) +const auth = useReadonlyStream(gqlAuth$, { + authType: "none", + authActive: true, +}) const url = useStream(gqlURL$, "", setGQLURL) const onConnectClick = () => { if (!connected.value) { - props.conn.connect(url.value, headers.value as any) + props.conn.connect(url.value, headers.value as any, auth.value) platform.analytics?.logHoppRequestRunToAnalytics({ platform: "graphql-schema", diff --git a/packages/hoppscotch-common/src/components/smart/EnvInput.vue b/packages/hoppscotch-common/src/components/smart/EnvInput.vue index 23218ec8b..3aec9783b 100644 --- a/packages/hoppscotch-common/src/components/smart/EnvInput.vue +++ b/packages/hoppscotch-common/src/components/smart/EnvInput.vue @@ -44,6 +44,7 @@ const props = withDefaults( envs?: { key: string; value: string; source: string }[] | null focus?: boolean selectTextOnMount?: boolean + environmentHighlights?: boolean readonly?: boolean }>(), { @@ -53,6 +54,7 @@ const props = withDefaults( envs: null, focus: false, readonly: false, + environmentHighlights: true, } ) @@ -142,7 +144,7 @@ const initView = (el: any) => { tooltips({ position: "absolute", }), - envTooltipPlugin, + props.environmentHighlights ? envTooltipPlugin : [], placeholderExt(props.placeholder), EditorView.domEventHandlers({ paste(ev) { diff --git a/packages/hoppscotch-common/src/helpers/GQLConnection.ts b/packages/hoppscotch-common/src/helpers/GQLConnection.ts index 0f9bdf581..15d17b5cd 100644 --- a/packages/hoppscotch-common/src/helpers/GQLConnection.ts +++ b/packages/hoppscotch-common/src/helpers/GQLConnection.ts @@ -99,7 +99,7 @@ export class GQLConnection { private timeoutSubscription: any - public connect(url: string, headers: GQLHeader[]) { + public connect(url: string, headers: GQLHeader[], auth: HoppGQLAuth) { if (this.connected$.value) { throw new Error( "A connection is already running. Close it before starting another." @@ -110,7 +110,7 @@ export class GQLConnection { this.connected$.next(true) const poll = async () => { - await this.getSchema(url, headers) + await this.getSchema(url, headers, auth) this.timeoutSubscription = setTimeout(() => { poll() }, GQL_SCHEMA_POLL_INTERVAL) @@ -135,7 +135,11 @@ export class GQLConnection { this.schema$.next(null) } - private async getSchema(url: string, headers: GQLHeader[]) { + private async getSchema( + url: string, + reqHeaders: GQLHeader[], + auth: HoppGQLAuth + ) { try { this.isLoading$.next(true) @@ -143,10 +147,38 @@ export class GQLConnection { query: getIntrospectionQuery(), }) + const headers = reqHeaders.filter((x) => x.active && x.key !== "") + + // TODO: Support a better b64 implementation than btoa ? + if (auth.authType === "basic") { + const username = auth.username + const password = auth.password + + headers.push({ + active: true, + key: "Authorization", + value: `Basic ${btoa(`${username}:${password}`)}`, + }) + } else if (auth.authType === "bearer" || auth.authType === "oauth-2") { + headers.push({ + active: true, + key: "Authorization", + value: `Bearer ${auth.token}`, + }) + } else if (auth.authType === "api-key") { + const { key, value, addTo } = auth + + if (addTo === "Headers") { + headers.push({ + active: true, + key, + value, + }) + } + } + const finalHeaders: Record = {} - headers - .filter((x) => x.active && x.key !== "") - .forEach((x) => (finalHeaders[x.key] = x.value)) + headers.forEach((x) => (finalHeaders[x.key] = x.value)) const reqOptions = { method: "POST", diff --git a/packages/hoppscotch-common/src/newstore/GQLSession.ts b/packages/hoppscotch-common/src/newstore/GQLSession.ts index e9a761688..81149dacc 100644 --- a/packages/hoppscotch-common/src/newstore/GQLSession.ts +++ b/packages/hoppscotch-common/src/newstore/GQLSession.ts @@ -275,7 +275,4 @@ export const gqlResponse$ = gqlSessionStore.subject$.pipe( distinctUntilChanged() ) -export const gqlAuth$ = gqlSessionStore.subject$.pipe( - pluck("request", "auth"), - distinctUntilChanged() -) +export const gqlAuth$ = gqlSessionStore.subject$.pipe(pluck("request", "auth")) diff --git a/packages/hoppscotch-common/src/newstore/localpersistence.ts b/packages/hoppscotch-common/src/newstore/localpersistence.ts index 386c7f8c2..c67776b4d 100644 --- a/packages/hoppscotch-common/src/newstore/localpersistence.ts +++ b/packages/hoppscotch-common/src/newstore/localpersistence.ts @@ -47,6 +47,8 @@ import { loadTabsFromPersistedState, persistableTabState, } from "~/helpers/rest/tab" +import { debounceTime } from "rxjs" +import { gqlSessionStore, setGQLSession } from "./GQLSession" function checkAndMigrateOldSettings() { if (window.localStorage.getItem("selectedEnvIndex")) { @@ -333,12 +335,35 @@ export function setupRESTTabsPersistence() { ) } +// temporary persistence for GQL session +export function setupGQLPersistence() { + try { + const state = window.localStorage.getItem("gqlState") + if (state) { + const data = JSON.parse(state) + data["schema"] = "" + data["response"] = "" + setGQLSession(data) + } + } catch (e) { + console.error( + `Failed parsing persisted GraphQL state, state:`, + window.localStorage.getItem("gqlState") + ) + } + + gqlSessionStore.subject$.pipe(debounceTime(500)).subscribe((state) => { + window.localStorage.setItem("gqlState", JSON.stringify(state)) + }) +} + export function setupLocalPersistence() { checkAndMigrateOldSettings() setupLocalStatePersistence() setupSettingsPersistence() setupRESTTabsPersistence() + setupGQLPersistence() setupHistoryPersistence() setupCollectionsPersistence() setupGlobalEnvsPersistence() diff --git a/packages/hoppscotch-common/src/pages/graphql.vue b/packages/hoppscotch-common/src/pages/graphql.vue index 171525e3c..a42798781 100644 --- a/packages/hoppscotch-common/src/pages/graphql.vue +++ b/packages/hoppscotch-common/src/pages/graphql.vue @@ -14,12 +14,10 @@