Add an authorization tab for GraphQL (#2125)
Co-authored-by: Rishabh Agarwal <rishabh2001agarwal@gmail.com> Co-authored-by: Rishabh Agarwal <45998880+RishabhAgarwal-2001@users.noreply.github.com> Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
This commit is contained in:
@@ -123,6 +123,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref } from "@nuxtjs/composition-api"
|
||||
import { HoppGQLRequest, makeGQLRequest } from "@hoppscotch/data"
|
||||
import { cloneDeep } from "lodash"
|
||||
import { removeGraphqlRequest } from "~/newstore/collections"
|
||||
import { setGQLSession } from "~/newstore/GQLSession"
|
||||
|
||||
@@ -177,13 +178,16 @@ export default defineComponent({
|
||||
this.pick()
|
||||
} else {
|
||||
setGQLSession({
|
||||
request: makeGQLRequest({
|
||||
name: this.$props.request.name,
|
||||
url: this.$props.request.url,
|
||||
query: this.$props.request.query,
|
||||
headers: this.$props.request.headers,
|
||||
variables: this.$props.request.variables,
|
||||
}),
|
||||
request: cloneDeep(
|
||||
makeGQLRequest({
|
||||
name: this.$props.request.name,
|
||||
url: this.$props.request.url,
|
||||
query: this.$props.request.query,
|
||||
headers: this.$props.request.headers,
|
||||
variables: this.$props.request.variables,
|
||||
auth: this.$props.request.auth,
|
||||
})
|
||||
),
|
||||
schema: "",
|
||||
response: "",
|
||||
})
|
||||
|
||||
322
packages/hoppscotch-app/components/graphql/Authorization.vue
Normal file
322
packages/hoppscotch-app/components/graphql/Authorization.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<div class="flex flex-col flex-1">
|
||||
<div
|
||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("authorization.type") }}
|
||||
</label>
|
||||
<tippy
|
||||
ref="authTypeOptions"
|
||||
interactive
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<span class="select-wrapper">
|
||||
<ButtonSecondary
|
||||
class="pr-8 ml-2 rounded-none"
|
||||
:label="authName"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
<SmartItem
|
||||
label="None"
|
||||
:icon="
|
||||
authName === 'None'
|
||||
? 'radio_button_checked'
|
||||
: 'radio_button_unchecked'
|
||||
"
|
||||
:active="authName === 'None'"
|
||||
@click.native="
|
||||
() => {
|
||||
authType = 'none'
|
||||
authTypeOptions.tippy().hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
label="Basic Auth"
|
||||
:icon="
|
||||
authName === 'Basic Auth'
|
||||
? 'radio_button_checked'
|
||||
: 'radio_button_unchecked'
|
||||
"
|
||||
:active="authName === 'Basic Auth'"
|
||||
@click.native="
|
||||
() => {
|
||||
authType = 'basic'
|
||||
authTypeOptions.tippy().hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
label="Bearer Token"
|
||||
:icon="
|
||||
authName === 'Bearer'
|
||||
? 'radio_button_checked'
|
||||
: 'radio_button_unchecked'
|
||||
"
|
||||
:active="authName === 'Bearer'"
|
||||
@click.native="
|
||||
() => {
|
||||
authType = 'bearer'
|
||||
authTypeOptions.tippy().hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
label="OAuth 2.0"
|
||||
:icon="
|
||||
authName === 'OAuth 2.0'
|
||||
? 'radio_button_checked'
|
||||
: 'radio_button_unchecked'
|
||||
"
|
||||
:active="authName === 'OAuth 2.0'"
|
||||
@click.native="
|
||||
() => {
|
||||
authType = 'oauth-2'
|
||||
authTypeOptions.tippy().hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
label="API key"
|
||||
:icon="
|
||||
authName === 'API key'
|
||||
? 'radio_button_checked'
|
||||
: 'radio_button_unchecked'
|
||||
"
|
||||
:active="authName === 'API key'"
|
||||
@click.native="
|
||||
() => {
|
||||
authType = 'api-key'
|
||||
authTypeOptions.tippy().hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
<div class="flex">
|
||||
<!-- <SmartCheckbox
|
||||
:on="!URLExcludes.auth"
|
||||
@change="setExclude('auth', !$event)"
|
||||
>
|
||||
{{ $t("authorization.include_in_url") }}
|
||||
</SmartCheckbox> -->
|
||||
<SmartCheckbox
|
||||
:on="authActive"
|
||||
class="px-2"
|
||||
@change="authActive = !authActive"
|
||||
>
|
||||
{{ $t("state.enabled") }}
|
||||
</SmartCheckbox>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/features/authorization"
|
||||
blank
|
||||
:title="$t('app.wiki')"
|
||||
svg="help-circle"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.clear')"
|
||||
svg="trash-2"
|
||||
@click.native="clearContent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="authType === 'none'"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<img
|
||||
:src="`/images/states/${$colorMode.value}/login.svg`"
|
||||
loading="lazy"
|
||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
||||
:alt="`${$t('empty.authorization')}`"
|
||||
/>
|
||||
<span class="pb-4 text-center">
|
||||
{{ $t("empty.authorization") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
outline
|
||||
:label="$t('app.documentation')"
|
||||
to="https://docs.hoppscotch.io/features/authorization"
|
||||
blank
|
||||
svg="external-link"
|
||||
reverse
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="flex flex-1 border-b border-dividerLight">
|
||||
<div class="w-2/3 border-r border-dividerLight">
|
||||
<div v-if="authType === 'basic'">
|
||||
<div class="flex flex-1 border-b border-dividerLight">
|
||||
<SmartEnvInput
|
||||
v-model="basicUsername"
|
||||
:placeholder="$t('authorization.username')"
|
||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-1 border-b border-dividerLight">
|
||||
<SmartEnvInput
|
||||
v-model="basicPassword"
|
||||
:placeholder="$t('authorization.password')"
|
||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="authType === 'bearer'">
|
||||
<div class="flex flex-1 border-b border-dividerLight">
|
||||
<SmartEnvInput
|
||||
v-model="bearerToken"
|
||||
placeholder="Token"
|
||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="authType === 'oauth-2'">
|
||||
<div class="flex flex-1 border-b border-dividerLight">
|
||||
<SmartEnvInput
|
||||
v-model="oauth2Token"
|
||||
placeholder="Token"
|
||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||
/>
|
||||
</div>
|
||||
<HttpOAuth2Authorization />
|
||||
</div>
|
||||
<div v-if="authType === 'api-key'">
|
||||
<div class="flex flex-1 border-b border-dividerLight">
|
||||
<SmartEnvInput
|
||||
v-model="apiKey"
|
||||
placeholder="Key"
|
||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-1 border-b border-dividerLight">
|
||||
<SmartEnvInput
|
||||
v-model="apiValue"
|
||||
placeholder="Value"
|
||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center border-b border-dividerLight">
|
||||
<label class="ml-4 text-secondaryLight">
|
||||
{{ $t("authorization.pass_key_by") }}
|
||||
</label>
|
||||
<tippy
|
||||
ref="addToOptions"
|
||||
interactive
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<span class="select-wrapper">
|
||||
<ButtonSecondary
|
||||
:label="addTo || $t('state.none')"
|
||||
class="pr-8 ml-2 rounded-none"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
<SmartItem
|
||||
:icon="
|
||||
addTo === 'Headers'
|
||||
? 'radio_button_checked'
|
||||
: 'radio_button_unchecked'
|
||||
"
|
||||
:active="addTo === 'Headers'"
|
||||
:label="'Headers'"
|
||||
@click.native="
|
||||
() => {
|
||||
addTo = 'Headers'
|
||||
addToOptions.tippy().hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
:icon="
|
||||
addTo === 'Query params'
|
||||
? 'radio_button_checked'
|
||||
: 'radio_button_unchecked'
|
||||
"
|
||||
:active="addTo === 'Query params'"
|
||||
:label="'Query params'"
|
||||
@click.native="
|
||||
() => {
|
||||
addTo = 'Query params'
|
||||
addToOptions.tippy().hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="sticky h-full p-4 overflow-auto bg-primary top-upperTertiaryStickyFold min-w-46 max-w-1/3 z-9"
|
||||
>
|
||||
<div class="pb-2 text-secondaryLight">
|
||||
{{ $t("helpers.authorization") }}
|
||||
</div>
|
||||
<SmartAnchor
|
||||
class="link"
|
||||
:label="`${$t('authorization.learn')} \xA0 →`"
|
||||
to="https://docs.hoppscotch.io/features/authorization"
|
||||
blank
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, Ref } from "@nuxtjs/composition-api"
|
||||
import {
|
||||
HoppGQLAuthAPIKey,
|
||||
HoppGQLAuthBasic,
|
||||
HoppGQLAuthBearer,
|
||||
HoppGQLAuthOAuth2,
|
||||
} from "@hoppscotch/data"
|
||||
import { pluckRef, useStream } from "~/helpers/utils/composables"
|
||||
import { gqlAuth$, setGQLAuth } from "~/newstore/GQLSession"
|
||||
|
||||
const auth = useStream(
|
||||
gqlAuth$,
|
||||
{ authType: "none", authActive: true },
|
||||
setGQLAuth
|
||||
)
|
||||
const authType = pluckRef(auth, "authType")
|
||||
const authName = computed(() => {
|
||||
if (authType.value === "basic") return "Basic Auth"
|
||||
else if (authType.value === "bearer") return "Bearer"
|
||||
else if (authType.value === "oauth-2") return "OAuth 2.0"
|
||||
else if (authType.value === "api-key") return "API key"
|
||||
else return "None"
|
||||
})
|
||||
const authActive = pluckRef(auth, "authActive")
|
||||
const basicUsername = pluckRef(auth as Ref<HoppGQLAuthBasic>, "username")
|
||||
const basicPassword = pluckRef(auth as Ref<HoppGQLAuthBasic>, "password")
|
||||
const bearerToken = pluckRef(auth as Ref<HoppGQLAuthBearer>, "token")
|
||||
const oauth2Token = pluckRef(auth as Ref<HoppGQLAuthOAuth2>, "token")
|
||||
const apiKey = pluckRef(auth as Ref<HoppGQLAuthAPIKey>, "key")
|
||||
const apiValue = pluckRef(auth as Ref<HoppGQLAuthAPIKey>, "value")
|
||||
const addTo = pluckRef(auth as Ref<HoppGQLAuthAPIKey>, "addTo")
|
||||
if (typeof addTo.value === "undefined") {
|
||||
addTo.value = "Headers"
|
||||
apiKey.value = ""
|
||||
apiValue.value = ""
|
||||
}
|
||||
|
||||
const clearContent = () => {
|
||||
auth.value = {
|
||||
authType: "none",
|
||||
authActive: true,
|
||||
}
|
||||
}
|
||||
const authTypeOptions = ref<any | null>(null)
|
||||
const addToOptions = ref<any | null>(null)
|
||||
</script>
|
||||
@@ -260,6 +260,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</SmartTab>
|
||||
<SmartTab :id="'authorization'" :label="`${t('tab.authorization')}`">
|
||||
<GraphqlAuthorization />
|
||||
</SmartTab>
|
||||
</SmartTabs>
|
||||
<CollectionsSaveRequest
|
||||
mode="graphql"
|
||||
@@ -296,11 +299,13 @@ import {
|
||||
useToast,
|
||||
} from "~/helpers/utils/composables"
|
||||
import {
|
||||
gqlAuth$,
|
||||
gqlHeaders$,
|
||||
gqlQuery$,
|
||||
gqlResponse$,
|
||||
gqlURL$,
|
||||
gqlVariables$,
|
||||
setGQLAuth,
|
||||
setGQLHeaders,
|
||||
setGQLQuery,
|
||||
setGQLResponse,
|
||||
@@ -353,6 +358,12 @@ useCodemirror(bulkEditor, bulkHeaders, {
|
||||
// The functional headers list (the headers actually in the system)
|
||||
const headers = useStream(gqlHeaders$, [], setGQLHeaders) as Ref<GQLHeader[]>
|
||||
|
||||
const auth = useStream(
|
||||
gqlAuth$,
|
||||
{ authType: "none", authActive: true },
|
||||
setGQLAuth
|
||||
)
|
||||
|
||||
// The UI representation of the headers list (has the empty end header)
|
||||
const workingHeaders = ref<Array<GQLHeader & { id: number }>>([
|
||||
{
|
||||
@@ -602,12 +613,14 @@ const runQuery = async () => {
|
||||
const runHeaders = clone(headers.value)
|
||||
const runQuery = clone(gqlQueryString.value)
|
||||
const runVariables = clone(variableString.value)
|
||||
const runAuth = clone(auth.value)
|
||||
|
||||
const responseText = await props.conn.runQuery(
|
||||
runURL,
|
||||
runHeaders,
|
||||
runQuery,
|
||||
runVariables
|
||||
runVariables,
|
||||
runAuth
|
||||
)
|
||||
const duration = Date.now() - startTime
|
||||
|
||||
@@ -623,6 +636,7 @@ const runQuery = async () => {
|
||||
query: runQuery,
|
||||
headers: runHeaders,
|
||||
variables: runVariables,
|
||||
auth: runAuth,
|
||||
}),
|
||||
response: response.value,
|
||||
star: false,
|
||||
|
||||
@@ -210,6 +210,7 @@ import {
|
||||
useToast,
|
||||
} from "~/helpers/utils/composables"
|
||||
import {
|
||||
setGQLAuth,
|
||||
setGQLHeaders,
|
||||
setGQLQuery,
|
||||
setGQLResponse,
|
||||
@@ -451,6 +452,10 @@ const handleUseHistory = (entry: GQLHistoryEntry) => {
|
||||
setGQLQuery(gqlQueryString)
|
||||
setGQLVariables(variableString)
|
||||
setGQLResponse(responseText)
|
||||
setGQLAuth({
|
||||
authType: "none",
|
||||
authActive: true,
|
||||
})
|
||||
props.conn.reset()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "@nuxtjs/composition-api"
|
||||
import { makeGQLRequest } from "@hoppscotch/data"
|
||||
import { cloneDeep } from "lodash"
|
||||
import { setGQLSession } from "~/newstore/GQLSession"
|
||||
import { GQLHistoryEntry } from "~/newstore/history"
|
||||
|
||||
@@ -79,13 +80,16 @@ const query = computed(() =>
|
||||
|
||||
const useEntry = () => {
|
||||
setGQLSession({
|
||||
request: makeGQLRequest({
|
||||
name: props.entry.request.name,
|
||||
url: props.entry.request.url,
|
||||
headers: props.entry.request.headers,
|
||||
query: props.entry.request.query,
|
||||
variables: props.entry.request.variables,
|
||||
}),
|
||||
request: cloneDeep(
|
||||
makeGQLRequest({
|
||||
name: props.entry.request.name,
|
||||
url: props.entry.request.url,
|
||||
headers: props.entry.request.headers,
|
||||
query: props.entry.request.query,
|
||||
variables: props.entry.request.variables,
|
||||
auth: props.entry.request.auth,
|
||||
})
|
||||
),
|
||||
schema: "",
|
||||
response: props.entry.response,
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
GraphQLInterfaceType,
|
||||
} from "graphql"
|
||||
import { distinctUntilChanged, map } from "rxjs/operators"
|
||||
import { GQLHeader } from "@hoppscotch/data"
|
||||
import { GQLHeader, HoppGQLAuth } from "@hoppscotch/data"
|
||||
import { sendNetworkRequest } from "./network"
|
||||
|
||||
const GQL_SCHEMA_POLL_INTERVAL = 7000
|
||||
@@ -182,15 +182,36 @@ export class GQLConnection {
|
||||
url: string,
|
||||
headers: GQLHeader[],
|
||||
query: string,
|
||||
variables: string
|
||||
variables: string,
|
||||
auth: HoppGQLAuth
|
||||
) {
|
||||
const finalHeaders: Record<string, string> = {}
|
||||
|
||||
const parsedVariables = JSON.parse(variables || "{}")
|
||||
|
||||
const params: Record<string, string> = {}
|
||||
|
||||
if (auth.authActive) {
|
||||
if (auth.authType === "basic") {
|
||||
const username = auth.username
|
||||
const password = auth.password
|
||||
finalHeaders.Authorization = `Basic ${btoa(`${username}:${password}`)}`
|
||||
} else if (auth.authType === "bearer" || auth.authType === "oauth-2") {
|
||||
finalHeaders.Authorization = `Bearer ${auth.token}`
|
||||
} else if (auth.authType === "api-key") {
|
||||
const { key, value, addTo } = auth
|
||||
if (addTo === "Headers") {
|
||||
finalHeaders[key] = value
|
||||
} else if (addTo === "Query params") {
|
||||
params[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headers
|
||||
.filter((item) => item.active && item.key !== "")
|
||||
.forEach(({ key, value }) => (finalHeaders[key] = value))
|
||||
|
||||
const parsedVariables = JSON.parse(variables || "{}")
|
||||
|
||||
const reqOptions = {
|
||||
method: "post",
|
||||
url,
|
||||
@@ -202,6 +223,9 @@ export class GQLConnection {
|
||||
query,
|
||||
variables: parsedVariables,
|
||||
}),
|
||||
params: {
|
||||
...params,
|
||||
},
|
||||
}
|
||||
|
||||
const res = await sendNetworkRequest(reqOptions)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { distinctUntilChanged, pluck } from "rxjs/operators"
|
||||
import { GQLHeader, HoppGQLRequest, makeGQLRequest } from "@hoppscotch/data"
|
||||
import {
|
||||
GQLHeader,
|
||||
HoppGQLRequest,
|
||||
makeGQLRequest,
|
||||
HoppGQLAuth,
|
||||
} from "@hoppscotch/data"
|
||||
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
|
||||
import { useStream } from "~/helpers/utils/composables"
|
||||
|
||||
@@ -26,6 +31,10 @@ export const defaultGQLSession: GQLSession = {
|
||||
}
|
||||
}
|
||||
`,
|
||||
auth: {
|
||||
authType: "none",
|
||||
authActive: true,
|
||||
},
|
||||
}),
|
||||
schema: "",
|
||||
response: "",
|
||||
@@ -112,6 +121,14 @@ const dispatchers = defineDispatchers({
|
||||
response: newResponse,
|
||||
}
|
||||
},
|
||||
setAuth(curr: GQLSession, { newAuth }: { newAuth: HoppGQLAuth }) {
|
||||
return {
|
||||
request: {
|
||||
...curr.request,
|
||||
auth: newAuth,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export const gqlSessionStore = new DispatchingStore(
|
||||
@@ -223,6 +240,15 @@ export function useGQLRequestName() {
|
||||
})
|
||||
}
|
||||
|
||||
export function setGQLAuth(newAuth: HoppGQLAuth) {
|
||||
gqlSessionStore.dispatch({
|
||||
dispatcher: "setAuth",
|
||||
payload: {
|
||||
newAuth,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const gqlName$ = gqlSessionStore.subject$.pipe(
|
||||
pluck("request", "name"),
|
||||
distinctUntilChanged()
|
||||
@@ -248,3 +274,8 @@ export const gqlResponse$ = gqlSessionStore.subject$.pipe(
|
||||
pluck("response"),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
|
||||
export const gqlAuth$ = gqlSessionStore.subject$.pipe(
|
||||
pluck("request", "auth"),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
translateToNewRequest,
|
||||
HoppGQLRequest,
|
||||
translateToGQLRequest,
|
||||
GQL_REQ_SCHEMA_VERSION,
|
||||
} from "@hoppscotch/data"
|
||||
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
|
||||
import { completedRESTResponse$ } from "./RESTSession"
|
||||
@@ -83,10 +84,12 @@ export function translateToNewRESTHistory(x: any): RESTHistoryEntry {
|
||||
}
|
||||
|
||||
export function translateToNewGQLHistory(x: any): GQLHistoryEntry {
|
||||
if (x.v === 1) return x
|
||||
if (x.v === 1 && x.request.v === GQL_REQ_SCHEMA_VERSION) return x
|
||||
|
||||
// Legacy
|
||||
const request = translateToGQLRequest(x)
|
||||
const request = x.request
|
||||
? translateToGQLRequest(x.request)
|
||||
: translateToGQLRequest(x)
|
||||
const star = x.star ?? false
|
||||
const response = x.response ?? ""
|
||||
const updatedOn = x.updatedOn ?? ""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hoppscotch/data",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"description": "Data Types, Validations and Migrations for Hoppscotch Public Data Structures",
|
||||
"main": "dist/index.js",
|
||||
"module": "true",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HoppGQLRequest, translateToGQLRequest } from "../graphql";
|
||||
import { GQL_REQ_SCHEMA_VERSION, HoppGQLRequest, translateToGQLRequest } from "../graphql";
|
||||
import { HoppRESTRequest, translateToNewRequest } from "../rest";
|
||||
|
||||
const CURRENT_COLL_SCHEMA_VER = 1
|
||||
@@ -65,7 +65,7 @@ export function translateToNewRESTCollection(
|
||||
export function translateToNewGQLCollection(
|
||||
x: any
|
||||
): HoppCollection<HoppGQLRequest> {
|
||||
if (x.v && x.v === 1) return x
|
||||
if (x.v && x.v === GQL_REQ_SCHEMA_VERSION) return x
|
||||
|
||||
// Legacy
|
||||
const name = x.name ?? "Untitled"
|
||||
|
||||
43
packages/hoppscotch-data/src/graphql/HoppGQLAuth.ts
Normal file
43
packages/hoppscotch-data/src/graphql/HoppGQLAuth.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export type HoppGQLAuthNone = {
|
||||
authType: "none"
|
||||
}
|
||||
|
||||
export type HoppGQLAuthBasic = {
|
||||
authType: "basic"
|
||||
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export type HoppGQLAuthBearer = {
|
||||
authType: "bearer"
|
||||
|
||||
token: string
|
||||
}
|
||||
|
||||
export type HoppGQLAuthOAuth2 = {
|
||||
authType: "oauth-2"
|
||||
|
||||
token: string
|
||||
oidcDiscoveryURL: string
|
||||
authURL: string
|
||||
accessTokenURL: string
|
||||
clientID: string
|
||||
scope: string
|
||||
}
|
||||
|
||||
export type HoppGQLAuthAPIKey = {
|
||||
authType: "api-key"
|
||||
|
||||
key: string
|
||||
value: string
|
||||
addTo: string
|
||||
}
|
||||
|
||||
export type HoppGQLAuth = { authActive: boolean } & (
|
||||
| HoppGQLAuthNone
|
||||
| HoppGQLAuthBasic
|
||||
| HoppGQLAuthBearer
|
||||
| HoppGQLAuthOAuth2
|
||||
| HoppGQLAuthAPIKey
|
||||
)
|
||||
@@ -1,3 +1,9 @@
|
||||
import { HoppGQLAuth } from "./HoppGQLAuth"
|
||||
|
||||
export * from "./HoppGQLAuth"
|
||||
|
||||
export const GQL_REQ_SCHEMA_VERSION = 2
|
||||
|
||||
export type GQLHeader = {
|
||||
key: string
|
||||
value: string
|
||||
@@ -11,10 +17,11 @@ export type HoppGQLRequest = {
|
||||
headers: GQLHeader[]
|
||||
query: string
|
||||
variables: string
|
||||
auth: HoppGQLAuth
|
||||
}
|
||||
|
||||
export function translateToGQLRequest(x: any): HoppGQLRequest {
|
||||
if (x.v && x.v === 1) return x
|
||||
if (x.v && x.v === GQL_REQ_SCHEMA_VERSION) return x
|
||||
|
||||
// Old request
|
||||
const name = x.name ?? "Untitled"
|
||||
@@ -22,20 +29,25 @@ export function translateToGQLRequest(x: any): HoppGQLRequest {
|
||||
const headers = x.headers ?? []
|
||||
const query = x.query ?? ""
|
||||
const variables = x.variables ?? []
|
||||
const auth = x.auth ?? {
|
||||
authType: "none",
|
||||
authActive: true,
|
||||
}
|
||||
|
||||
return {
|
||||
v: 1,
|
||||
v: GQL_REQ_SCHEMA_VERSION,
|
||||
name,
|
||||
url,
|
||||
headers,
|
||||
query,
|
||||
variables,
|
||||
auth
|
||||
}
|
||||
}
|
||||
|
||||
export function makeGQLRequest(x: Omit<HoppGQLRequest, "v">) {
|
||||
export function makeGQLRequest(x: Omit<HoppGQLRequest, "v">): HoppGQLRequest {
|
||||
return {
|
||||
v: 1,
|
||||
v: GQL_REQ_SCHEMA_VERSION,
|
||||
...x,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user