fix: resolve multiple UI issues and account for description field in syncing context (#4309)
* fix: collection properties header bulk update editor bug * chore: add migration step while resolving `headers` in the syncing context Resolve type errors. * fix: prevent inifinite loading state in add environments modal Toggle back the loading state if attempting to create an environment from a team workspace without specifying a name. * fix: tab change when clicking computed auth * fix: ensure tab change action works in GQL headers view `Go to Authorization tab` action. * chore: account for REST params while adding description fields Writing to store after pulling from the syncing context. --------- Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
<HttpHeaders
|
<HttpHeaders
|
||||||
v-model="editableCollection"
|
v-model="editableCollection"
|
||||||
:is-collection-property="true"
|
:is-collection-property="true"
|
||||||
|
@change-tab="changeOptionTab"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="bg-bannerInfo px-4 py-2 flex items-center sticky bottom-0"
|
class="bg-bannerInfo px-4 py-2 flex items-center sticky bottom-0"
|
||||||
@@ -136,6 +137,7 @@ import { PersistenceService } from "~/services/persistence"
|
|||||||
import IconCheck from "~icons/lucide/check"
|
import IconCheck from "~icons/lucide/check"
|
||||||
import IconCopy from "~icons/lucide/copy"
|
import IconCopy from "~icons/lucide/copy"
|
||||||
import IconHelpCircle from "~icons/lucide/help-circle"
|
import IconHelpCircle from "~icons/lucide/help-circle"
|
||||||
|
import { RESTOptionTabs } from "../http/RequestOptions.vue"
|
||||||
|
|
||||||
const persistenceService = useService(PersistenceService)
|
const persistenceService = useService(PersistenceService)
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
@@ -268,6 +270,10 @@ const hideModal = () => {
|
|||||||
emit("hide-modal")
|
emit("hide-modal")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeOptionTab = (e: RESTOptionTabs) => {
|
||||||
|
activeTab.value = e
|
||||||
|
}
|
||||||
|
|
||||||
const copyCollectionID = () => {
|
const copyCollectionID = () => {
|
||||||
copyToClipboard(props.editingProperties.path)
|
copyToClipboard(props.editingProperties.path)
|
||||||
copyIcon.value = IconCheck
|
copyIcon.value = IconCheck
|
||||||
|
|||||||
@@ -354,7 +354,9 @@ const saveEnvironment = async () => {
|
|||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
|
|
||||||
if (!editingName.value) {
|
if (!editingName.value) {
|
||||||
|
isLoading.value = false
|
||||||
toast.error(`${t("environment.invalid_name")}`)
|
toast.error(`${t("environment.invalid_name")}`)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,19 +127,15 @@
|
|||||||
:icon="masking ? IconEye : IconEyeOff"
|
:icon="masking ? IconEye : IconEyeOff"
|
||||||
@click="toggleMask()"
|
@click="toggleMask()"
|
||||||
/>
|
/>
|
||||||
<HoppButtonSecondary
|
<div v-else class="aspect-square w-8"></div>
|
||||||
v-else
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:icon="IconArrowUpRight"
|
|
||||||
:title="t('request.go_to_authorization_tab')"
|
|
||||||
class="cursor-auto text-primary hover:text-primary"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:icon="IconArrowUpRight"
|
:icon="IconArrowUpRight"
|
||||||
:title="t('request.go_to_authorization_tab')"
|
:title="t('request.go_to_authorization_tab')"
|
||||||
|
@click="changeTab"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -235,6 +231,7 @@ import { useColorMode } from "@composables/theming"
|
|||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
import {
|
import {
|
||||||
GQLHeader,
|
GQLHeader,
|
||||||
|
HoppGQLAuth,
|
||||||
HoppGQLRequest,
|
HoppGQLRequest,
|
||||||
parseRawKeyValueEntriesE,
|
parseRawKeyValueEntriesE,
|
||||||
rawKeyValueEntriesToString,
|
rawKeyValueEntriesToString,
|
||||||
@@ -267,6 +264,7 @@ import IconLock from "~icons/lucide/lock"
|
|||||||
import IconPlus from "~icons/lucide/plus"
|
import IconPlus from "~icons/lucide/plus"
|
||||||
import IconTrash2 from "~icons/lucide/trash-2"
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
import IconWrapText from "~icons/lucide/wrap-text"
|
import IconWrapText from "~icons/lucide/wrap-text"
|
||||||
|
import { GQLOptionTabs } from "./RequestOptions.vue"
|
||||||
|
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
@@ -281,6 +279,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "update:modelValue", value: HoppGQLRequest): void
|
(e: "update:modelValue", value: HoppGQLRequest): void
|
||||||
|
(e: "change-tab", value: GQLOptionTabs): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const request = useVModel(props, "modelValue", emit)
|
const request = useVModel(props, "modelValue", emit)
|
||||||
@@ -645,7 +644,7 @@ const inheritedProperties = computed(() => {
|
|||||||
|
|
||||||
const computedAuthHeader = getComputedAuthHeaders(
|
const computedAuthHeader = getComputedAuthHeaders(
|
||||||
request.value,
|
request.value,
|
||||||
props.inheritedProperties.auth.inheritedAuth
|
props.inheritedProperties.auth.inheritedAuth as HoppGQLAuth
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -678,8 +677,5 @@ const mask = (header: any) => {
|
|||||||
return header.header.value
|
return header.header.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// const changeTab = (tab: ComputedHeader["source"]) => {
|
const changeTab = () => emit("change-tab", "authorization")
|
||||||
// if (tab === "auth") emit("change-tab", "authorization")
|
|
||||||
// else emit("change-tab", "bodyParams")
|
|
||||||
// }
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
<GraphqlHeaders
|
<GraphqlHeaders
|
||||||
v-model="request"
|
v-model="request"
|
||||||
:inherited-properties="inheritedProperties"
|
:inherited-properties="inheritedProperties"
|
||||||
|
@change-tab="changeOptionTab"
|
||||||
/>
|
/>
|
||||||
</HoppSmartTab>
|
</HoppSmartTab>
|
||||||
<HoppSmartTab :id="'authorization'" :label="`${t('tab.authorization')}`">
|
<HoppSmartTab :id="'authorization'" :label="`${t('tab.authorization')}`">
|
||||||
@@ -261,6 +262,11 @@ const saveRequest = () => {
|
|||||||
const clearGQLQuery = () => {
|
const clearGQLQuery = () => {
|
||||||
request.value.query = ""
|
request.value.query = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeOptionTab = (e: GQLOptionTabs) => {
|
||||||
|
selectedOptionTab.value = e
|
||||||
|
}
|
||||||
|
|
||||||
defineActionHandler("request.send-cancel", runQuery)
|
defineActionHandler("request.send-cancel", runQuery)
|
||||||
defineActionHandler("request.save", saveRequest)
|
defineActionHandler("request.save", saveRequest)
|
||||||
defineActionHandler("request.save-as", () => {
|
defineActionHandler("request.save-as", () => {
|
||||||
|
|||||||
@@ -49,8 +49,14 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="bulkMode" class="h-full relative w-full flex flex-col flex-1">
|
<div v-if="bulkMode" class="h-full relative w-full flex flex-col flex-1">
|
||||||
<div ref="bulkEditor" class="absolute inset-0"></div>
|
<div
|
||||||
|
ref="bulkEditor"
|
||||||
|
:class="{
|
||||||
|
'absolute inset-0': !isCollectionProperty,
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<draggable
|
<draggable
|
||||||
|
|||||||
@@ -48,8 +48,11 @@ import {
|
|||||||
} from "@hoppscotch/common/newstore/collections"
|
} from "@hoppscotch/common/newstore/collections"
|
||||||
import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient"
|
import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient"
|
||||||
import {
|
import {
|
||||||
|
GQLHeader,
|
||||||
HoppCollection,
|
HoppCollection,
|
||||||
HoppGQLRequest,
|
HoppGQLRequest,
|
||||||
|
HoppRESTHeaders,
|
||||||
|
HoppRESTParam,
|
||||||
HoppRESTRequest,
|
HoppRESTRequest,
|
||||||
} from "@hoppscotch/data"
|
} from "@hoppscotch/data"
|
||||||
import { gqlCollectionsSyncer } from "./gqlCollections.sync"
|
import { gqlCollectionsSyncer } from "./gqlCollections.sync"
|
||||||
@@ -100,12 +103,22 @@ type ExportedUserCollectionGQL = {
|
|||||||
data: string
|
data: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addDescriptionField(
|
||||||
|
candidate: HoppRESTHeaders | GQLHeader[] | HoppRESTParam[]
|
||||||
|
) {
|
||||||
|
return candidate.map((item) => ({
|
||||||
|
...item,
|
||||||
|
description: "description" in item ? item.description : "",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
function exportedCollectionToHoppCollection(
|
function exportedCollectionToHoppCollection(
|
||||||
collection: ExportedUserCollectionREST | ExportedUserCollectionGQL,
|
collection: ExportedUserCollectionREST | ExportedUserCollectionGQL,
|
||||||
collectionType: "REST" | "GQL"
|
collectionType: "REST" | "GQL"
|
||||||
): HoppCollection {
|
): HoppCollection {
|
||||||
if (collectionType == "REST") {
|
if (collectionType == "REST") {
|
||||||
const restCollection = collection as ExportedUserCollectionREST
|
const restCollection = collection as ExportedUserCollectionREST
|
||||||
|
|
||||||
const data =
|
const data =
|
||||||
restCollection.data && restCollection.data !== "null"
|
restCollection.data && restCollection.data !== "null"
|
||||||
? JSON.parse(restCollection.data)
|
? JSON.parse(restCollection.data)
|
||||||
@@ -113,9 +126,10 @@ function exportedCollectionToHoppCollection(
|
|||||||
auth: { authType: "inherit", authActive: false },
|
auth: { authType: "inherit", authActive: false },
|
||||||
headers: [],
|
headers: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: restCollection.id,
|
id: restCollection.id,
|
||||||
v: 2,
|
v: 3,
|
||||||
name: restCollection.name,
|
name: restCollection.name,
|
||||||
folders: restCollection.folders.map((folder) =>
|
folders: restCollection.folders.map((folder) =>
|
||||||
exportedCollectionToHoppCollection(folder, collectionType)
|
exportedCollectionToHoppCollection(folder, collectionType)
|
||||||
@@ -140,26 +154,31 @@ function exportedCollectionToHoppCollection(
|
|||||||
testScript,
|
testScript,
|
||||||
requestVariables,
|
requestVariables,
|
||||||
} = request
|
} = request
|
||||||
|
|
||||||
|
const resolvedParams = addDescriptionField(params)
|
||||||
|
const resolvedHeaders = addDescriptionField(headers)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
v,
|
v,
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
endpoint,
|
endpoint,
|
||||||
method,
|
method,
|
||||||
params,
|
params: resolvedParams,
|
||||||
requestVariables: requestVariables,
|
requestVariables,
|
||||||
auth,
|
auth,
|
||||||
headers,
|
headers: resolvedHeaders,
|
||||||
body,
|
body,
|
||||||
preRequestScript,
|
preRequestScript,
|
||||||
testScript,
|
testScript,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
auth: data.auth,
|
auth: data.auth,
|
||||||
headers: data.headers,
|
headers: addDescriptionField(data.headers),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const gqlCollection = collection as ExportedUserCollectionGQL
|
const gqlCollection = collection as ExportedUserCollectionGQL
|
||||||
|
|
||||||
const data =
|
const data =
|
||||||
gqlCollection.data && gqlCollection.data !== "null"
|
gqlCollection.data && gqlCollection.data !== "null"
|
||||||
? JSON.parse(gqlCollection.data)
|
? JSON.parse(gqlCollection.data)
|
||||||
@@ -170,25 +189,34 @@ function exportedCollectionToHoppCollection(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: gqlCollection.id,
|
id: gqlCollection.id,
|
||||||
v: 2,
|
v: 3,
|
||||||
name: gqlCollection.name,
|
name: gqlCollection.name,
|
||||||
folders: gqlCollection.folders.map((folder) =>
|
folders: gqlCollection.folders.map((folder) =>
|
||||||
exportedCollectionToHoppCollection(folder, collectionType)
|
exportedCollectionToHoppCollection(folder, collectionType)
|
||||||
),
|
),
|
||||||
requests: gqlCollection.requests.map(
|
requests: gqlCollection.requests.map((request) => {
|
||||||
({ v, auth, headers, name, id, query, url, variables }) => ({
|
const requestParsedResult = HoppGQLRequest.safeParse(request)
|
||||||
|
if (requestParsedResult.type === "ok") {
|
||||||
|
return requestParsedResult.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const { v, auth, headers, name, id, query, url, variables } = request
|
||||||
|
|
||||||
|
const resolvedHeaders = addDescriptionField(headers)
|
||||||
|
|
||||||
|
return {
|
||||||
id,
|
id,
|
||||||
v,
|
v,
|
||||||
auth,
|
auth,
|
||||||
headers,
|
headers: resolvedHeaders,
|
||||||
name,
|
name,
|
||||||
query,
|
query,
|
||||||
url,
|
url,
|
||||||
variables,
|
variables,
|
||||||
})
|
}
|
||||||
) as HoppGQLRequest[],
|
}),
|
||||||
auth: data.auth,
|
auth: data.auth,
|
||||||
headers: data.headers,
|
headers: addDescriptionField(data.headers),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,17 +377,17 @@ function setupUserCollectionCreatedSubscription() {
|
|||||||
name: res.right.userCollectionCreated.title,
|
name: res.right.userCollectionCreated.title,
|
||||||
folders: [],
|
folders: [],
|
||||||
requests: [],
|
requests: [],
|
||||||
v: 2,
|
v: 3,
|
||||||
auth: data.auth,
|
auth: data.auth,
|
||||||
headers: data.headers,
|
headers: addDescriptionField(data.headers),
|
||||||
})
|
})
|
||||||
: addRESTCollection({
|
: addRESTCollection({
|
||||||
name: res.right.userCollectionCreated.title,
|
name: res.right.userCollectionCreated.title,
|
||||||
folders: [],
|
folders: [],
|
||||||
requests: [],
|
requests: [],
|
||||||
v: 2,
|
v: 3,
|
||||||
auth: data.auth,
|
auth: data.auth,
|
||||||
headers: data?.headers,
|
headers: addDescriptionField(data.headers),
|
||||||
})
|
})
|
||||||
|
|
||||||
const localIndex = collectionStore.value.state.length - 1
|
const localIndex = collectionStore.value.state.length - 1
|
||||||
|
|||||||
Reference in New Issue
Block a user