RequestOptions.vue (hoppscotch-2.0.0) | : | RequestOptions.vue (hoppscotch-2.1.0) | ||
---|---|---|---|---|
<template> | <template> | |||
<div> | <div> | |||
<SmartTabs styles="sticky top-upperPrimaryStickyFold z-10"> | <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> | |||
<SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> | <template #actions> | |||
<ButtonSecondary | ||||
:label="`${$t('request.run')}`" | ||||
svg="play" | ||||
class="rounded-none !text-accent" | ||||
@click.native="runQuery()" | ||||
/> | ||||
<ButtonSecondary | ||||
ref="saveRequest" | ||||
:label="`${$t('request.save')}`" | ||||
class="rounded-none" | ||||
@click.native="saveRequest" | ||||
/> | ||||
</template> | ||||
<SmartTab :id="'query'" :label="`${$t('tab.query')}`" :selected="true"> | ||||
<AppSection label="query"> | <AppSection label="query"> | |||
<div | <div | |||
class=" | class=" | |||
bg-primary | bg-primary | |||
border-b border-dividerLight | border-b border-dividerLight | |||
flex flex-1 | flex flex-1 | |||
top-upperSecondaryStickyFold | top-upperSecondaryStickyFold | |||
pl-4 | pl-4 | |||
z-10 | z-10 | |||
sticky | sticky | |||
items-center | items-center | |||
justify-between | justify-between | |||
gqlRunQuery | gqlRunQuery | |||
" | " | |||
> | > | |||
<label class="font-semibold text-secondaryLight"> | <label class="font-semibold text-secondaryLight"> | |||
{{ $t("request.query") }} | {{ $t("request.query") }} | |||
</label> | </label> | |||
<div class="flex"> | <div class="flex"> | |||
<ButtonSecondary | <ButtonSecondary | |||
:label="$t('request.run')" | v-tippy="{ theme: 'tooltip' }" | |||
icon="play_arrow" | to="https://docs.hoppscotch.io" | |||
class="rounded-none !text-accent" | blank | |||
@click.native="runQuery()" | :title="$t('app.wiki')" | |||
svg="help-circle" | ||||
/> | /> | |||
<ButtonSecondary | <ButtonSecondary | |||
v-tippy="{ theme: 'tooltip' }" | v-tippy="{ theme: 'tooltip' }" | |||
:title="$t('action.copy')" | :title="$t('action.copy')" | |||
:icon="copyQueryIcon" | :svg="`${copyQueryIcon}`" | |||
@click.native="copyQuery" | @click.native="copyQuery" | |||
/> | /> | |||
<ButtonSecondary | <ButtonSecondary | |||
v-tippy="{ theme: 'tooltip' }" | v-tippy="{ theme: 'tooltip' }" | |||
:title="`${$t( | :title="$t('action.prettify')" | |||
'action.prettify' | :svg="`${prettifyQueryIcon}`" | |||
)} <kbd>${getSpecialKey()}</kbd><kbd>P</kbd>`" | ||||
:icon="prettifyQueryIcon" | ||||
@click.native="prettifyQuery" | @click.native="prettifyQuery" | |||
/> | /> | |||
<ButtonSecondary | ||||
ref="saveRequest" | ||||
v-tippy="{ theme: 'tooltip' }" | ||||
:title="$t('request.save')" | ||||
icon="create_new_folder" | ||||
@click.native="saveRequest" | ||||
/> | ||||
</div> | </div> | |||
</div> | </div> | |||
<GraphqlQueryEditor | <div ref="queryEditor"></div> | |||
ref="queryEditor" | ||||
v-model="gqlQueryString" | ||||
:on-run-g-q-l-query="runQuery" | ||||
:options="{ | ||||
maxLines: Infinity, | ||||
minLines: 16, | ||||
autoScrollEditorIntoView: true, | ||||
showPrintMargin: false, | ||||
useWorker: false, | ||||
}" | ||||
styles="border-b border-dividerLight" | ||||
@update-query="updateQuery" | ||||
/> | ||||
</AppSection> | </AppSection> | |||
</SmartTab> | </SmartTab> | |||
<SmartTab :id="'variables'" :label="$t('tab.variables')"> | <SmartTab :id="'variables'" :label="`${$t('tab.variables')}`"> | |||
<AppSection label="variables"> | <AppSection label="variables"> | |||
<div | <div | |||
class=" | class=" | |||
bg-primary | bg-primary | |||
border-b border-dividerLight | border-b border-dividerLight | |||
flex flex-1 | flex flex-1 | |||
top-upperSecondaryStickyFold | top-upperSecondaryStickyFold | |||
pl-4 | pl-4 | |||
z-10 | z-10 | |||
sticky | sticky | |||
items-center | items-center | |||
justify-between | justify-between | |||
" | " | |||
> | > | |||
<label class="font-semibold text-secondaryLight"> | <label class="font-semibold text-secondaryLight"> | |||
{{ $t("request.variables") }} | {{ $t("request.variables") }} | |||
</label> | </label> | |||
<div class="flex"> | <div class="flex"> | |||
<ButtonSecondary | <ButtonSecondary | |||
v-tippy="{ theme: 'tooltip' }" | v-tippy="{ theme: 'tooltip' }" | |||
to="https://docs.hoppscotch.io" | ||||
blank | ||||
:title="$t('app.wiki')" | ||||
svg="help-circle" | ||||
/> | ||||
<ButtonSecondary | ||||
v-tippy="{ theme: 'tooltip' }" | ||||
:title="$t('action.copy')" | :title="$t('action.copy')" | |||
:icon="copyVariablesIcon" | :svg="`${copyVariablesIcon}`" | |||
@click.native="copyVariables" | @click.native="copyVariables" | |||
/> | /> | |||
</div> | </div> | |||
</div> | </div> | |||
<SmartAceEditor | <div ref="variableEditor"></div> | |||
ref="variableEditor" | ||||
v-model="variableString" | ||||
:lang="'json'" | ||||
:options="{ | ||||
maxLines: Infinity, | ||||
minLines: 16, | ||||
autoScrollEditorIntoView: true, | ||||
showPrintMargin: false, | ||||
useWorker: false, | ||||
}" | ||||
styles="border-b border-dividerLight" | ||||
/> | ||||
</AppSection> | </AppSection> | |||
</SmartTab> | </SmartTab> | |||
<SmartTab :id="'headers'" :label="$t('tab.headers')"> | <SmartTab :id="'headers'" :label="`${$t('tab.headers')}`"> | |||
<AppSection label="headers"> | <AppSection label="headers"> | |||
<div | <div | |||
class=" | class=" | |||
bg-primary | bg-primary | |||
border-b border-dividerLight | border-b border-dividerLight | |||
flex flex-1 | flex flex-1 | |||
top-upperSecondaryStickyFold | top-upperSecondaryStickyFold | |||
pl-4 | pl-4 | |||
z-10 | z-10 | |||
sticky | sticky | |||
items-center | items-center | |||
justify-between | justify-between | |||
" | " | |||
> | > | |||
<label class="font-semibold text-secondaryLight"> | <label class="font-semibold text-secondaryLight"> | |||
{{ $t("tab.headers") }} | {{ $t("tab.headers") }} | |||
</label> | </label> | |||
<div class="flex"> | <div class="flex"> | |||
<ButtonSecondary | <ButtonSecondary | |||
v-tippy="{ theme: 'tooltip' }" | v-tippy="{ theme: 'tooltip' }" | |||
to="https://docs.hoppscotch.io" | ||||
blank | ||||
:title="$t('app.wiki')" | ||||
svg="help-circle" | ||||
/> | ||||
<ButtonSecondary | ||||
v-tippy="{ theme: 'tooltip' }" | ||||
:title="$t('action.clear_all')" | :title="$t('action.clear_all')" | |||
icon="clear_all" | svg="trash-2" | |||
:disabled="Boolean(bulkMode)" | ||||
@click.native="headers = []" | @click.native="headers = []" | |||
/> | /> | |||
<ButtonSecondary | <ButtonSecondary | |||
v-tippy="{ theme: 'tooltip' }" | v-tippy="{ theme: 'tooltip' }" | |||
:title="$t('state.bulk_mode')" | ||||
svg="edit" | ||||
:class="{ '!text-accent': bulkMode }" | ||||
@click.native="bulkMode = !bulkMode" | ||||
/> | ||||
<ButtonSecondary | ||||
v-tippy="{ theme: 'tooltip' }" | ||||
:title="$t('add.new')" | :title="$t('add.new')" | |||
icon="add" | svg="plus" | |||
:disabled="Boolean(bulkMode)" | ||||
@click.native="addRequestHeader" | @click.native="addRequestHeader" | |||
/> | /> | |||
</div> | </div> | |||
</div> | </div> | |||
<div | <div v-if="bulkMode" ref="bulkEditor"></div> | |||
v-for="(header, index) in headers" | <div v-else> | |||
:key="`header-${index}`" | <div | |||
class=" | v-for="(header, index) in headers" | |||
divide-x divide-dividerLight | :key="`header-${String(index)}`" | |||
border-b border-dividerLight | class=" | |||
flex | divide-x divide-dividerLight | |||
" | border-b border-dividerLight | |||
> | ||||
<SmartAutoComplete | ||||
:placeholder="$t('count.header', { count: index + 1 })" | ||||
:source="commonHeaders" | ||||
:spellcheck="false" | ||||
:value="header.key" | ||||
autofocus | ||||
styles=" | ||||
bg-transparent | ||||
flex | flex | |||
flex-1 | ||||
py-1 | ||||
px-4 | ||||
truncate | ||||
focus:outline-none | ||||
" | ||||
@input=" | ||||
updateGQLHeader(index, { | ||||
key: $event, | ||||
value: header.value, | ||||
active: header.active, | ||||
}) | ||||
" | ||||
/> | ||||
<input | ||||
class="bg-transparent flex flex-1 py-2 px-4" | ||||
:placeholder="$t('count.value', { count: index + 1 })" | ||||
:name="`value ${index}`" | ||||
:value="header.value" | ||||
autofocus | ||||
@change=" | ||||
updateGQLHeader(index, { | ||||
key: header.key, | ||||
value: $event.target.value, | ||||
active: header.active, | ||||
}) | ||||
" | " | |||
/> | > | |||
<span> | <SmartAutoComplete | |||
<ButtonSecondary | :placeholder="`${$t('count.header', { count: index + 1 })}`" | |||
v-tippy="{ theme: 'tooltip' }" | :source="commonHeaders" | |||
:title=" | :spellcheck="false" | |||
header.hasOwnProperty('active') | :value="header.key" | |||
? header.active | autofocus | |||
? $t('action.turn_off') | styles=" | |||
: $t('action.turn_on') | bg-transparent | |||
: $t('action.turn_off') | flex | |||
flex-1 | ||||
py-1 | ||||
px-4 | ||||
truncate | ||||
focus:outline-none | ||||
" | " | |||
:icon=" | @input=" | |||
header.hasOwnProperty('active') | updateGQLHeader(index, { | |||
? header.active | key: $event, | |||
? 'check_circle_outline' | value: header.value, | |||
: 'radio_button_unchecked' | active: header.active, | |||
: 'check_circle_outline' | }) | |||
" | " | |||
color="green" | /> | |||
@click.native=" | <input | |||
class="bg-transparent flex flex-1 py-2 px-4" | ||||
:placeholder="`${$t('count.value', { count: index + 1 })}`" | ||||
:name="`value ${String(index)}`" | ||||
:value="header.value" | ||||
autofocus | ||||
@change=" | ||||
updateGQLHeader(index, { | updateGQLHeader(index, { | |||
key: header.key, | key: header.key, | |||
value: header.value, | value: $event.target.value, | |||
active: !header.active, | active: header.active, | |||
}) | }) | |||
" | " | |||
/> | /> | |||
</span> | <span> | |||
<span> | <ButtonSecondary | |||
v-tippy="{ theme: 'tooltip' }" | ||||
:title=" | ||||
header.hasOwnProperty('active') | ||||
? header.active | ||||
? $t('action.turn_off') | ||||
: $t('action.turn_on') | ||||
: $t('action.turn_off') | ||||
" | ||||
:svg=" | ||||
header.hasOwnProperty('active') | ||||
? header.active | ||||
? 'check-circle' | ||||
: 'circle' | ||||
: 'check-circle' | ||||
" | ||||
color="green" | ||||
@click.native=" | ||||
updateGQLHeader(index, { | ||||
key: header.key, | ||||
value: header.value, | ||||
active: !header.active, | ||||
}) | ||||
" | ||||
/> | ||||
</span> | ||||
<span> | ||||
<ButtonSecondary | ||||
v-tippy="{ theme: 'tooltip' }" | ||||
:title="$t('action.remove')" | ||||
svg="trash" | ||||
color="red" | ||||
@click.native="removeRequestHeader(index)" | ||||
/> | ||||
</span> | ||||
</div> | ||||
<div | ||||
v-if="headers.length === 0" | ||||
class=" | ||||
flex flex-col | ||||
text-secondaryLight | ||||
p-4 | ||||
items-center | ||||
justify-center | ||||
" | ||||
> | ||||
<img | ||||
:src="`/images/states/${$colorMode.value}/add_category.svg`" | ||||
loading="lazy" | ||||
class=" | ||||
flex-col | ||||
my-4 | ||||
object-contain object-center | ||||
h-16 | ||||
w-16 | ||||
inline-flex | ||||
" | ||||
/> | ||||
<span class="text-center pb-4"> | ||||
{{ $t("empty.headers") }} | ||||
</span> | ||||
<ButtonSecondary | <ButtonSecondary | |||
v-tippy="{ theme: 'tooltip' }" | :label="`${$t('add.new')}`" | |||
:title="$t('action.remove')" | filled | |||
icon="remove_circle_outline" | svg="plus" | |||
color="red" | @click.native="addRequestHeader" | |||
@click.native="removeRequestHeader(index)" | ||||
/> | /> | |||
</span> | </div> | |||
</div> | ||||
<div | ||||
v-if="headers.length === 0" | ||||
class=" | ||||
flex flex-col | ||||
text-secondaryLight | ||||
p-4 | ||||
items-center | ||||
justify-center | ||||
" | ||||
> | ||||
<span class="text-center pb-4"> | ||||
{{ $t("empty.headers") }} | ||||
</span> | ||||
<ButtonSecondary | ||||
:label="$t('add.new')" | ||||
filled | ||||
icon="add" | ||||
@click.native="addRequestHeader" | ||||
/> | ||||
</div> | </div> | |||
</AppSection> | </AppSection> | |||
</SmartTab> | </SmartTab> | |||
</SmartTabs> | </SmartTabs> | |||
<CollectionsSaveRequest | <CollectionsSaveRequest | |||
mode="graphql" | mode="graphql" | |||
:show="showSaveRequestModal" | :show="Boolean(showSaveRequestModal)" | |||
@hide-modal="hideRequestModal" | @hide-modal="hideRequestModal" | |||
/> | /> | |||
</div> | </div> | |||
</template> | </template> | |||
<script lang="ts"> | <script setup lang="ts"> | |||
import { | import { onMounted, ref, useContext, watch } from "@nuxtjs/composition-api" | |||
defineComponent, | ||||
onMounted, | ||||
PropType, | ||||
ref, | ||||
useContext, | ||||
watch, | ||||
} from "@nuxtjs/composition-api" | ||||
import clone from "lodash/clone" | import clone from "lodash/clone" | |||
import { getPlatformSpecialKey } from "~/helpers/platformutils" | import * as gql from "graphql" | |||
import { copyToClipboard } from "~/helpers/utils/clipboard" | import { copyToClipboard } from "~/helpers/utils/clipboard" | |||
import { | import { | |||
useNuxt, | useNuxt, | |||
useReadonlyStream, | useReadonlyStream, | |||
useStream, | useStream, | |||
} from "~/helpers/utils/composables" | } from "~/helpers/utils/composables" | |||
import { | import { | |||
addGQLHeader, | addGQLHeader, | |||
gqlHeaders$, | gqlHeaders$, | |||
gqlQuery$, | gqlQuery$, | |||
skipping to change at line 298 | skipping to change at line 311 | |||
setGQLResponse, | setGQLResponse, | |||
setGQLVariables, | setGQLVariables, | |||
updateGQLHeader, | updateGQLHeader, | |||
} from "~/newstore/GQLSession" | } from "~/newstore/GQLSession" | |||
import { commonHeaders } from "~/helpers/headers" | import { commonHeaders } from "~/helpers/headers" | |||
import { GQLConnection } from "~/helpers/GQLConnection" | import { GQLConnection } from "~/helpers/GQLConnection" | |||
import { makeGQLHistoryEntry, addGraphqlHistoryEntry } from "~/newstore/history" | import { makeGQLHistoryEntry, addGraphqlHistoryEntry } from "~/newstore/history" | |||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics" | import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics" | |||
import { getCurrentStrategyID } from "~/helpers/network" | import { getCurrentStrategyID } from "~/helpers/network" | |||
import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest" | import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest" | |||
import { useCodemirror } from "~/helpers/editor/codemirror" | ||||
import "codemirror/mode/javascript/javascript" | ||||
import "~/helpers/editor/modes/graphql" | ||||
import jsonLinter from "~/helpers/editor/linting/json" | ||||
import { createGQLQueryLinter } from "~/helpers/editor/linting/gqlQuery" | ||||
import queryCompleter from "~/helpers/editor/completion/gqlQuery" | ||||
const props = defineProps<{ | ||||
conn: GQLConnection | ||||
}>() | ||||
const { | ||||
$toast, | ||||
app: { i18n }, | ||||
} = useContext() | ||||
const t = i18n.t.bind(i18n) | ||||
const nuxt = useNuxt() | ||||
const bulkMode = ref(false) | ||||
const bulkHeaders = ref("") | ||||
watch(bulkHeaders, () => { | ||||
try { | ||||
const transformation = bulkHeaders.value.split("\n").map((item) => ({ | ||||
key: item.substring(0, item.indexOf(":")).trim().replace(/^\/\//, ""), | ||||
value: item.substring(item.indexOf(":") + 1).trim(), | ||||
active: !item.trim().startsWith("//"), | ||||
})) | ||||
setGQLHeaders(transformation) | ||||
} catch (e) { | ||||
$toast.error(`${t("error.something_went_wrong")}`, { | ||||
icon: "error_outline", | ||||
}) | ||||
console.error(e) | ||||
} | ||||
}) | ||||
export default defineComponent({ | const url = useReadonlyStream(gqlURL$, "") | |||
props: { | const gqlQueryString = useStream(gqlQuery$, "", setGQLQuery) | |||
conn: { | const variableString = useStream(gqlVariables$, "", setGQLVariables) | |||
type: Object as PropType<GQLConnection>, | const headers = useStream(gqlHeaders$, [], setGQLHeaders) | |||
required: true, | ||||
}, | const bulkEditor = ref<any | null>(null) | |||
useCodemirror(bulkEditor, bulkHeaders, { | ||||
extendedEditorConfig: { | ||||
mode: "text/x-yaml", | ||||
placeholder: `${t("state.bulk_mode_placeholder")}`, | ||||
}, | }, | |||
setup(props) { | linter: null, | |||
const { | completer: null, | |||
$toast, | }) | |||
app: { i18n }, | ||||
} = useContext() | ||||
const t = i18n.t.bind(i18n) | ||||
const nuxt = useNuxt() | ||||
const url = useReadonlyStream(gqlURL$, "") | ||||
const gqlQueryString = useStream(gqlQuery$, "", setGQLQuery) | ||||
const variableString = useStream(gqlVariables$, "", setGQLVariables) | ||||
const headers = useStream(gqlHeaders$, [], setGQLHeaders) | ||||
const queryEditor = ref<any | null>(null) | ||||
const copyQueryIcon = ref("content_copy") | ||||
const prettifyQueryIcon = ref("photo_filter") | ||||
const copyVariablesIcon = ref("content_copy") | ||||
const showSaveRequestModal = ref(false) | ||||
const schema = useReadonlyStream(props.conn.schemaString$, "") | ||||
watch( | ||||
headers, | ||||
() => { | ||||
if ( | ||||
(headers.value[headers.value.length - 1]?.key !== "" || | ||||
headers.value[headers.value.length - 1]?.value !== "") && | ||||
headers.value.length | ||||
) | ||||
addRequestHeader() | ||||
}, | ||||
{ deep: true } | ||||
) | ||||
onMounted(() => { | const variableEditor = ref<any | null>(null) | |||
if (!headers.value?.length) { | ||||
addRequestHeader() | ||||
} | ||||
}) | ||||
const copyQuery = () => { | useCodemirror(variableEditor, variableString, { | |||
copyToClipboard(gqlQueryString.value) | extendedEditorConfig: { | |||
copyQueryIcon.value = "done" | mode: "application/ld+json", | |||
setTimeout(() => (copyQueryIcon.value = "content_copy"), 1000) | placeholder: `${t("request.variables")}`, | |||
} | }, | |||
linter: jsonLinter, | ||||
const response = useStream(gqlResponse$, "", setGQLResponse) | completer: null, | |||
}) | ||||
const runQuery = async () => { | ||||
const startTime = Date.now() | ||||
nuxt.value.$loading.start() | ||||
response.value = t("state.loading").toString() | ||||
try { | ||||
const runURL = clone(url.value) | ||||
const runHeaders = clone(headers.value) | ||||
const runQuery = clone(gqlQueryString.value) | ||||
const runVariables = clone(variableString.value) | ||||
const responseText = await props.conn.runQuery( | ||||
runURL, | ||||
runHeaders, | ||||
runQuery, | ||||
runVariables | ||||
) | ||||
const duration = Date.now() - startTime | ||||
nuxt.value.$loading.finish() | ||||
response.value = JSON.stringify(JSON.parse(responseText), null, 2) | ||||
addGraphqlHistoryEntry( | ||||
makeGQLHistoryEntry({ | ||||
request: makeGQLRequest({ | ||||
name: "", | ||||
url: runURL, | ||||
query: runQuery, | ||||
headers: runHeaders, | ||||
variables: runVariables, | ||||
}), | ||||
response: response.value, | ||||
star: false, | ||||
}) | ||||
) | ||||
$toast.success(t("state.finished_in", { duration }).toString(), { | ||||
icon: "done", | ||||
}) | ||||
} catch (e: any) { | ||||
response.value = `${e}. ${t("error.check_console_details")}` | ||||
nuxt.value.$loading.finish() | ||||
$toast.error(`${e} ${t("error.f12_details").toString()}`, { | ||||
icon: "error_outline", | ||||
}) | ||||
console.error(e) | ||||
} | ||||
logHoppRequestRunToAnalytics({ | const queryEditor = ref<any | null>(null) | |||
platform: "graphql-query", | const schemaString = useReadonlyStream(props.conn.schema$, null) | |||
strategy: getCurrentStrategyID(), | ||||
}) | ||||
} | ||||
const hideRequestModal = () => { | useCodemirror(queryEditor, gqlQueryString, { | |||
showSaveRequestModal.value = false | extendedEditorConfig: { | |||
} | mode: "graphql", | |||
placeholder: `${t("request.query")}`, | ||||
const prettifyQuery = () => { | }, | |||
queryEditor.value.prettifyQuery() | linter: createGQLQueryLinter(schemaString), | |||
prettifyQueryIcon.value = "done" | completer: queryCompleter(schemaString), | |||
setTimeout(() => (prettifyQueryIcon.value = "photo_filter"), 1000) | }) | |||
} | ||||
const saveRequest = () => { | ||||
showSaveRequestModal.value = true | ||||
} | ||||
// Why ? | ||||
const updateQuery = (updatedQuery: string) => { | ||||
gqlQueryString.value = updatedQuery | ||||
} | ||||
const copyVariables = () => { | ||||
copyToClipboard(variableString.value) | ||||
copyVariablesIcon.value = "done" | ||||
setTimeout(() => (copyVariablesIcon.value = "content_copy"), 1000) | ||||
} | ||||
const addRequestHeader = () => { | ||||
addGQLHeader({ | ||||
key: "", | ||||
value: "", | ||||
active: true, | ||||
}) | ||||
} | ||||
const removeRequestHeader = (index: number) => { | const copyQueryIcon = ref("copy") | |||
removeGQLHeader(index) | const prettifyQueryIcon = ref("align-left") | |||
} | const copyVariablesIcon = ref("copy") | |||
const showSaveRequestModal = ref(false) | ||||
watch( | ||||
headers, | ||||
() => { | ||||
if ( | ||||
(headers.value[headers.value.length - 1]?.key !== "" || | ||||
headers.value[headers.value.length - 1]?.value !== "") && | ||||
headers.value.length | ||||
) | ||||
addRequestHeader() | ||||
}, | ||||
{ deep: true } | ||||
) | ||||
return { | onMounted(() => { | |||
gqlQueryString, | if (!headers.value?.length) { | |||
variableString, | addRequestHeader() | |||
headers, | } | |||
copyQueryIcon, | }) | |||
prettifyQueryIcon, | ||||
copyVariablesIcon, | ||||
queryEditor, | const copyQuery = () => { | |||
copyToClipboard(gqlQueryString.value) | ||||
copyQueryIcon.value = "check" | ||||
setTimeout(() => (copyQueryIcon.value = "copy"), 1000) | ||||
} | ||||
const response = useStream(gqlResponse$, "", setGQLResponse) | ||||
const runQuery = async () => { | ||||
const startTime = Date.now() | ||||
nuxt.value.$loading.start() | ||||
response.value = "loading" | ||||
try { | ||||
const runURL = clone(url.value) | ||||
const runHeaders = clone(headers.value) | ||||
const runQuery = clone(gqlQueryString.value) | ||||
const runVariables = clone(variableString.value) | ||||
const responseText = await props.conn.runQuery( | ||||
runURL, | ||||
runHeaders, | ||||
runQuery, | ||||
runVariables | ||||
) | ||||
const duration = Date.now() - startTime | ||||
showSaveRequestModal, | nuxt.value.$loading.finish() | |||
hideRequestModal, | ||||
schema, | response.value = JSON.stringify(JSON.parse(responseText), null, 2) | |||
copyQuery, | addGraphqlHistoryEntry( | |||
runQuery, | makeGQLHistoryEntry({ | |||
prettifyQuery, | request: makeGQLRequest({ | |||
saveRequest, | name: "", | |||
updateQuery, | url: runURL, | |||
copyVariables, | query: runQuery, | |||
addRequestHeader, | headers: runHeaders, | |||
removeRequestHeader, | variables: runVariables, | |||
}), | ||||
getSpecialKey: getPlatformSpecialKey, | response: response.value, | |||
star: false, | ||||
commonHeaders, | }) | |||
updateGQLHeader, | ) | |||
} | ||||
}, | $toast.success(`${t("state.finished_in", { duration })}`, { | |||
}) | icon: "done", | |||
}) | ||||
} catch (e: any) { | ||||
response.value = `${e}` | ||||
nuxt.value.$loading.finish() | ||||
$toast.error( | ||||
`${t("error.something_went_wrong")}. ${t("error.check_console_details")}`, | ||||
{ | ||||
icon: "error_outline", | ||||
} | ||||
) | ||||
console.error(e) | ||||
} | ||||
logHoppRequestRunToAnalytics({ | ||||
platform: "graphql-query", | ||||
strategy: getCurrentStrategyID(), | ||||
}) | ||||
} | ||||
const hideRequestModal = () => { | ||||
showSaveRequestModal.value = false | ||||
} | ||||
const prettifyQuery = () => { | ||||
try { | ||||
gqlQueryString.value = gql.print(gql.parse(gqlQueryString.value)) | ||||
} catch (e) { | ||||
$toast.error(`${t("error.gql_prettify_invalid_query")}`, { | ||||
icon: "error_outline", | ||||
}) | ||||
} | ||||
prettifyQueryIcon.value = "check" | ||||
setTimeout(() => (prettifyQueryIcon.value = "align-left"), 1000) | ||||
} | ||||
const saveRequest = () => { | ||||
showSaveRequestModal.value = true | ||||
} | ||||
const copyVariables = () => { | ||||
copyToClipboard(variableString.value) | ||||
copyVariablesIcon.value = "check" | ||||
setTimeout(() => (copyVariablesIcon.value = "copy"), 1000) | ||||
} | ||||
const addRequestHeader = () => { | ||||
addGQLHeader({ | ||||
key: "", | ||||
value: "", | ||||
active: true, | ||||
}) | ||||
} | ||||
const removeRequestHeader = (index: number) => { | ||||
removeGQLHeader(index) | ||||
} | ||||
</script> | </script> | |||
End of changes. 40 change blocks. | ||||
323 lines changed or deleted | 359 lines changed or added |