refactor: iterations

This commit is contained in:
jamesgeorge007
2024-02-04 20:34:23 +05:30
parent 29e25b0ead
commit ab7df212c2
11 changed files with 1235 additions and 187 deletions

View File

@@ -22,20 +22,28 @@
</span> </span>
</span> </span>
</div> </div>
<div v-if="!collectionReadonly" class="flex"> <div class="flex">
<HoppButtonSecondary <HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
:icon="IconFilePlus" :icon="IconFilePlus"
:title="t('request.new')" :title="t('request.new')"
class="hidden group-hover:inline-flex" class="hidden group-hover:inline-flex"
@click="emit('add-request')" @click="
emit('add-request', {
path: collection.collectionID,
})
"
/> />
<HoppButtonSecondary <HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
:icon="IconFolderPlus" :icon="IconFolderPlus"
:title="t('folder.new')" :title="t('folder.new')"
class="hidden group-hover:inline-flex" class="hidden group-hover:inline-flex"
@click="emit('add-folder')" @click="
emit('add-folder', {
path: collection.collectionID,
})
"
/> />
<span> <span>
<tippy <tippy
@@ -69,7 +77,9 @@
:shortcut="['R']" :shortcut="['R']"
@click=" @click="
() => { () => {
emit('add-request') emit('add-request', {
path: collection.collectionID,
})
hide() hide()
} }
" "
@@ -81,7 +91,9 @@
:shortcut="['N']" :shortcut="['N']"
@click=" @click="
() => { () => {
emit('add-folder') emit('add-folder', {
path: collection.collectionID,
})
hide() hide()
} }
" "
@@ -105,8 +117,7 @@
:shortcut="['X']" :shortcut="['X']"
@click=" @click="
() => { () => {
emit('export-data'), emit('export-data'), hide()
collectionsType === 'my-collections' ? hide() : null
} }
" "
/> />
@@ -117,7 +128,9 @@
:shortcut="['⌫']" :shortcut="['⌫']"
@click=" @click="
() => { () => {
emit('remove-collection') emit('remove-collection', {
path: collection.collectionID,
})
hide() hide()
} }
" "
@@ -150,17 +163,31 @@ const t = useI18n()
const props = defineProps<{ const props = defineProps<{
collection: RESTCollectionViewCollection collection: RESTCollectionViewCollection
collectionReadonly: boolean
isOpen: boolean isOpen: boolean
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
(event: "toggle-children"): void (event: "toggle-children"): void
(event: "add-request"): void (
(event: "add-folder"): void event: "add-request",
payload: {
path: string
}
): void
(
event: "add-folder",
payload: {
path: string
}
): void
(event: "edit-collection"): void (event: "edit-collection"): void
(event: "export-data"): void (event: "export-data"): void
(event: "remove-collection"): void (
event: "remove-collection",
payload: {
path: string
}
): void
}>() }>()
const tippyActions = ref<TippyComponent | null>(null) const tippyActions = ref<TippyComponent | null>(null)

View File

@@ -6,6 +6,7 @@
> >
<div <div
class="pointer-events-auto flex min-w-0 flex-1 cursor-pointer items-center justify-center" class="pointer-events-auto flex min-w-0 flex-1 cursor-pointer items-center justify-center"
@click="selectRequest(request.requestID, request.request)"
> >
<span <span
class="pointer-events-none flex w-16 items-center justify-center truncate px-2" class="pointer-events-none flex w-16 items-center justify-center truncate px-2"
@@ -38,12 +39,13 @@
</span> </span>
</span> </span>
</div> </div>
<div v-if="!collectionReadonly" class="flex"> <div class="flex">
<HoppButtonSecondary <HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
:icon="IconRotateCCW" :icon="IconRotateCCW"
:title="t('action.restore')" :title="t('action.restore')"
class="hidden group-hover:inline-flex" class="hidden group-hover:inline-flex"
@click="selectRequest(request.requestID, request.request)"
/> />
<span> <span>
<tippy <tippy
@@ -75,7 +77,10 @@
:shortcut="['E']" :shortcut="['E']"
@click=" @click="
() => { () => {
emit('edit-request') emit('edit-request', {
requestPath: request.requestID,
request: request.request,
})
hide() hide()
} }
" "
@@ -87,7 +92,10 @@
:shortcut="['D']" :shortcut="['D']"
@click=" @click="
() => { () => {
emit('duplicate-request') emit('duplicate-request', {
requestPath: request.requestID,
request: request.request,
})
hide() hide()
} }
" "
@@ -124,12 +132,37 @@ import { RESTCollectionViewRequest } from "~/services/new-workspace/view"
import { computed, ref } from "vue" import { computed, ref } from "vue"
import { TippyComponent } from "vue-tippy" import { TippyComponent } from "vue-tippy"
import { getMethodLabelColorClassOf } from "~/helpers/rest/labelColoring" import { getMethodLabelColorClassOf } from "~/helpers/rest/labelColoring"
import { HoppRESTRequest } from "@hoppscotch/data"
const t = useI18n() const t = useI18n()
const props = defineProps<{ const props = defineProps<{
request: RESTCollectionViewRequest request: RESTCollectionViewRequest
collectionReadonly: boolean }>()
const emit = defineEmits<{
(
event: "duplicate-request",
payload: {
requestPath: string
request: HoppRESTRequest
}
): void
(
event: "edit-request",
payload: {
requestPath: string
request: HoppRESTRequest
}
): void
(event: "remove-request"): void
(
event: "select-request",
payload: {
requestPath: string
request: HoppRESTRequest
}
): void
}>() }>()
const tippyActions = ref<TippyComponent | null>(null) const tippyActions = ref<TippyComponent | null>(null)
@@ -141,4 +174,10 @@ const isActive = ref(true)
const requestLabelColor = computed(() => const requestLabelColor = computed(() =>
getMethodLabelColorClassOf(props.request) getMethodLabelColorClassOf(props.request)
) )
const selectRequest = (requestPath: string, request: HoppRESTRequest) =>
emit("select-request", {
requestPath,
request,
})
</script> </script>

View File

@@ -1,20 +1,10 @@
<template> <template>
<div v-if="collectionsAreReadonly !== undefined" class="flex flex-1 flex-col"> <div class="flex flex-1 flex-col">
<div <div
class="sticky z-10 flex flex-1 justify-between border-b border-dividerLight bg-primary" class="sticky z-10 flex flex-1 justify-between border-b border-dividerLight bg-primary"
:style="'top: var(--upper-primary-sticky-fold)'" :style="'top: var(--upper-primary-sticky-fold)'"
> >
<HoppButtonSecondary <HoppButtonSecondary
v-if="collectionsAreReadonly"
v-tippy="{ theme: 'tooltip' }"
disabled
class="!rounded-none"
:icon="IconPlus"
:title="t('team.no_access')"
:label="t('add.new')"
/>
<HoppButtonSecondary
v-else
:icon="IconPlus" :icon="IconPlus"
:label="t('add.new')" :label="t('add.new')"
class="!rounded-none" class="!rounded-none"
@@ -36,59 +26,114 @@
/> />
</span> </span>
</div> </div>
<div class="flex flex-1 flex-col"> <div class="flex flex-1 flex-col">
<HoppSmartTree :adapter="treeAdapter"> <HoppSmartTree :adapter="treeAdapter">
<template <template #content="{ node, toggleChildren, isOpen }">
#content="{ node, toggleChildren, isOpen, highlightChildren }"
>
<!-- TODO: Implement --> <!-- TODO: Implement -->
<NewCollectionsRestCollection <NewCollectionsRestCollection
v-if="node.data.type === 'collection'" v-if="node.data.type === 'collection'"
:collection="node.data.value" :collection="node.data.value"
:collection-readonly="collectionsAreReadonly"
:is-open="isOpen" :is-open="isOpen"
@add-request="addRequest"
@add-folder="addFolder"
@remove-collection="removeFolder"
@toggle-children="toggleChildren" @toggle-children="toggleChildren"
/> />
<NewCollectionsRestRequest
v-else-if="node.data.type === 'request'"
:request="node.data.value"
@duplicate-request="duplicateRequest"
@edit-request="editRequest"
@remove-request="removeRequest(node.data.value.requestID)"
@select-request="selectRequest"
/>
<div v-else @click="toggleChildren"> <div v-else @click="toggleChildren">
{{ node.data.value }} {{ node.data.value }}
</div> </div>
</template> </template>
<template #emptyNode="{ node }"> <template #emptyNode>
<!-- TODO: Implement --> <!-- TODO: Implement -->
<div>Empty Node!</div> <div>Empty Node!</div>
</template> </template>
</HoppSmartTree> </HoppSmartTree>
</div> </div>
<CollectionsAdd <CollectionsAdd
:show="showModalAdd" :show="showModalAdd"
:loading-state="modalLoadingState" :loading-state="modalLoadingState"
@submit="addNewRootCollection" @submit="addNewRootCollection"
@hide-modal="showModalAdd = false" @hide-modal="showModalAdd = false"
/> />
<CollectionsAddRequest
:show="showModalAddRequest"
:loading-state="modalLoadingState"
@add-request="onAddRequest"
@hide-modal="displayModalAddRequest(false)"
/>
<CollectionsAddFolder
:show="showModalAddFolder"
:loading-state="modalLoadingState"
@add-folder="onAddFolder"
@hide-modal="displayModalAddFolder(false)"
/>
<CollectionsEditRequest
v-model="editingRequestName"
:show="showModalEditRequest"
:loading-state="modalLoadingState"
@submit="onEditRequest"
@hide-modal="displayModalEditRequest(false)"
/>
<HoppSmartConfirmModal
:show="showConfirmModal"
:title="confirmModalTitle"
:loading-state="modalLoadingState"
@hide-modal="showConfirmModal = false"
@resolve="resolveConfirmModal"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as E from "fp-ts/lib/Either" import * as E from "fp-ts/lib/Either"
import { useService } from "dioc/vue"
import { markRaw, ref } from "vue"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import IconPlus from "~icons/lucide/plus" import { useToast } from "~/composables/toast"
import IconHelpCircle from "~icons/lucide/help-circle"
import IconImport from "~icons/lucide/folder-down"
import { HandleRef } from "~/services/new-workspace/handle"
import { Workspace } from "~/services/new-workspace/workspace"
import { computed, markRaw, ref } from "vue"
import { WorkspaceRESTCollectionTreeAdapter } from "~/helpers/adapters/WorkspaceRESTCollectionTreeAdapter" import { WorkspaceRESTCollectionTreeAdapter } from "~/helpers/adapters/WorkspaceRESTCollectionTreeAdapter"
import { NewWorkspaceService } from "~/services/new-workspace" import { NewWorkspaceService } from "~/services/new-workspace"
import { useService } from "dioc/vue" import { HandleRef } from "~/services/new-workspace/handle"
import { Workspace } from "~/services/new-workspace/workspace"
import { RESTTabService } from "~/services/tab/rest"
import IconImport from "~icons/lucide/folder-down"
import IconHelpCircle from "~icons/lucide/help-circle"
import IconPlus from "~icons/lucide/plus"
import {
resolveSaveContextOnCollectionReorder,
getFoldersByPath,
} from "~/helpers/collection/collection"
import {
navigateToFolderWithIndexPath,
restCollectionStore,
removeRESTFolder,
restCollections$,
} from "~/newstore/collections"
import { useReadonlyStream } from "~/composables/stream"
import { cloneDeep } from "lodash-es"
import { HoppRESTRequest } from "@hoppscotch/data"
const t = useI18n() const t = useI18n()
const toast = useToast()
const tabs = useService(RESTTabService)
const props = defineProps<{ const props = defineProps<{
workspaceHandle: HandleRef<Workspace> workspaceHandle: HandleRef<Workspace>
}>() }>()
const emit = defineEmits<{ defineEmits<{
(e: "display-modal-add"): void (e: "display-modal-add"): void
(e: "display-modal-import-export"): void (e: "display-modal-import-export"): void
}>() }>()
@@ -101,18 +146,48 @@ const treeAdapter = markRaw(
) )
) )
const collectionsAreReadonly = computed(() => {
if (props.workspaceHandle.value.type === "ok") {
return props.workspaceHandle.value.data.collectionsAreReadonly
}
return undefined
})
const showModalAdd = ref(false)
const modalLoadingState = ref(false) const modalLoadingState = ref(false)
async function addNewRootCollection(name: string) { const showModalAdd = ref(false)
const showModalAddRequest = ref(false)
const showModalAddFolder = ref(false)
const showModalEditRequest = ref(false)
const showConfirmModal = ref(false)
const editingFolderPath = ref<string | null>(null)
const editingRequest = ref<HoppRESTRequest | null>(null)
const editingRequestName = ref("")
const editingRequestIndex = ref<number | null>(null)
const confirmModalTitle = ref<string | null>(null)
const myCollections = useReadonlyStream(restCollections$, [], "deep")
const displayModalAddRequest = (show: boolean) => {
showModalAddRequest.value = show
if (!show) resetSelectedData()
}
const displayModalAddFolder = (show: boolean) => {
showModalAddFolder.value = show
if (!show) resetSelectedData()
}
const displayModalEditRequest = (show: boolean) => {
showModalEditRequest.value = show
if (!show) resetSelectedData()
}
const displayConfirmModal = (show: boolean) => {
showConfirmModal.value = show
if (!show) resetSelectedData()
}
const addNewRootCollection = async (name: string) => {
modalLoadingState.value = true modalLoadingState.value = true
const result = await workspaceService.createRESTRootCollection( const result = await workspaceService.createRESTRootCollection(
@@ -135,7 +210,379 @@ async function addNewRootCollection(name: string) {
showModalAdd.value = false showModalAdd.value = false
} }
const addRequest = (payload: { path: string }) => {
const { path } = payload
editingFolderPath.value = path
displayModalAddRequest(true)
}
const onAddRequest = async (requestName: string) => {
const path = editingFolderPath.value
if (!path) return
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
path
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const result = await workspaceService.createRESTRequest(
collHandle,
requestName,
path
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
displayModalAddRequest(false)
}
const addFolder = (payload: { path: string }) => {
const { path } = payload
editingFolderPath.value = path
displayModalAddFolder(true)
}
const onAddFolder = async (folderName: string) => {
const path = editingFolderPath.value
if (!path) return
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
path
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const result = await workspaceService.createRESTChildCollection(
collHandle,
folderName,
path
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
displayModalAddFolder(false)
}
const removeFolder = (payload: { path: string }) => {
const { path } = payload
editingFolderPath.value = path
confirmModalTitle.value = `${t("confirm.remove_folder")}`
displayConfirmModal(true)
}
const onRemoveFolder = () => {
const path = editingFolderPath.value
if (!path) return
const folderToRemove = path
? navigateToFolderWithIndexPath(
restCollectionStore.value.state,
path.split("/").map((i) => parseInt(i))
)
: undefined
removeRESTFolder(path, folderToRemove ? folderToRemove.id : undefined)
const parentFolder = path.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
resolveSaveContextOnCollectionReorder({
lastIndex: pathToLastIndex(path),
newIndex: -1,
folderPath: parentFolder,
length: getFoldersByPath(myCollections.value, parentFolder).length,
})
toast.success(t("state.deleted"))
displayConfirmModal(false)
}
const removeRequest = (requestIndex: string) => {
const folderPath = requestIndex.slice(0, -2)
const requestID = requestIndex[requestIndex.length - 1]
editingFolderPath.value = folderPath
editingRequestIndex.value = parseInt(requestID)
confirmModalTitle.value = `${t("confirm.remove_request")}`
displayConfirmModal(true)
}
const onRemoveRequest = async () => {
const path = editingFolderPath.value
const requestIndex = editingRequestIndex.value
if (path === null || requestIndex === null) return
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
path
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const result = await workspaceService.removeRESTRequest(
collHandle,
path,
requestIndex
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
toast.success(t("state.deleted"))
displayConfirmModal(false)
}
const selectRequest = async (payload: {
requestPath: string
request: HoppRESTRequest
}) => {
const { requestPath, request } = payload
const collPath = requestPath.slice(0, -2)
const requestIndex = requestPath[requestPath.length - 1]
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
collPath
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const result = await workspaceService.selectRESTRequest(
collHandle,
collPath,
requestIndex,
request
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
}
const duplicateRequest = async (payload: {
requestPath: string
request: HoppRESTRequest
}) => {
const { requestPath, request } = payload
const collPath = requestPath.slice(0, -2)
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
collPath
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const newRequest = {
...cloneDeep(request),
name: `${request.name} - ${t("action.duplicate")}`,
}
const result = await workspaceService.duplicateRESTRequest(
collHandle,
collPath,
newRequest
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
toast.success(t("request.duplicated"))
}
const editRequest = (payload: {
requestPath: string
request: HoppRESTRequest
}) => {
const { requestPath, request } = payload
const collPath = requestPath.slice(0, -2)
const requestIndex = requestPath[requestPath.length - 1]
editingRequest.value = request
editingRequestName.value = request.name ?? ""
editingFolderPath.value = collPath
editingRequestIndex.value = parseInt(requestIndex)
displayModalEditRequest(true)
}
const onEditRequest = async (newReqName: string) => {
const collPath = editingFolderPath.value
const requestIndex = editingRequestIndex.value
const request = editingRequest.value
if (collPath === null || requestIndex === null || !request) {
return
}
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
collPath
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const updatedRequest = {
...request,
name: newReqName || request.name,
}
const result = await workspaceService.editRESTRequest(
collHandle,
collPath,
requestIndex,
updatedRequest
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
displayModalEditRequest(false)
toast.success(t("request.renamed"))
}
function onImportExportClick() { function onImportExportClick() {
// TODO: Implement // TODO: Implement
} }
const resolveConfirmModal = (title: string | null) => {
if (title === `${t("confirm.remove_collection")}`) {
// onRemoveCollection()
} else if (title === `${t("confirm.remove_request")}`) {
onRemoveRequest()
} else if (title === `${t("confirm.remove_folder")}`) {
onRemoveFolder()
} else {
console.error(
`Confirm modal title ${title} is not handled by the component`
)
toast.error(t("error.something_went_wrong"))
displayConfirmModal(false)
}
}
const resetSelectedData = () => {
editingFolderPath.value = null
}
/**
* Used to get the index of the request from the path
* @param path The path of the request
* @returns The index of the request
*/
const pathToLastIndex = (path: string) => {
const pathArr = path.split("/")
return parseInt(pathArr[pathArr.length - 1])
}
</script> </script>

View File

@@ -2,7 +2,7 @@ import {
ChildrenResult, ChildrenResult,
SmartTreeAdapter, SmartTreeAdapter,
} from "@hoppscotch/ui/dist/src/helpers/treeAdapter" } from "@hoppscotch/ui/dist/src/helpers/treeAdapter"
import { Ref, computed, ref, watchEffect } from "vue" import { Ref, ref, watchEffect } from "vue"
import { NewWorkspaceService } from "~/services/new-workspace" import { NewWorkspaceService } from "~/services/new-workspace"
import { HandleRef } from "~/services/new-workspace/handle" import { HandleRef } from "~/services/new-workspace/handle"
import { import {

View File

@@ -1,13 +1,17 @@
import { pipe } from "fp-ts/function" import { pipe } from "fp-ts/function"
import * as O from "fp-ts/Option" import * as O from "fp-ts/Option"
import * as RR from "fp-ts/ReadonlyRecord" import * as RR from "fp-ts/ReadonlyRecord"
import { HoppRESTRequest } from "@hoppscotch/data"
export const REQUEST_METHOD_LABEL_COLORS = { export const REQUEST_METHOD_LABEL_COLORS = {
get: "var(--success-color)", get: "var(--method-get-color)",
post: "var(--warning-color)", post: "var(--method-post-color)",
put: "var(--blue-color)", put: "var(--method-put-color)",
delete: "var(--cl-error-color)", patch: "var(--method-patch-color)",
default: "#6b7280", delete: "var(--method-delete-color)",
head: "var(--method-head-color)",
options: "var(--method-options-color)",
default: "var(--method-default-color)",
} as const } as const
/** /**
@@ -15,10 +19,18 @@ export const REQUEST_METHOD_LABEL_COLORS = {
* @param request The HoppRESTRequest object to get the value for * @param request The HoppRESTRequest object to get the value for
* @returns The class value for the given HTTP VERB, if not, a generic verb class * @returns The class value for the given HTTP VERB, if not, a generic verb class
*/ */
export function getMethodLabelColorClassOf(request: { method: string }) { export function getMethodLabelColorClassOf(request: HoppRESTRequest) {
return pipe( return pipe(
REQUEST_METHOD_LABEL_COLORS, REQUEST_METHOD_LABEL_COLORS,
RR.lookup(request.method.toLowerCase()), RR.lookup(request.method.toLowerCase()),
O.getOrElseW(() => REQUEST_METHOD_LABEL_COLORS.default) O.getOrElseW(() => REQUEST_METHOD_LABEL_COLORS.default)
) )
} }
export function getMethodLabelColor(method: string) {
return pipe(
REQUEST_METHOD_LABEL_COLORS,
RR.lookup(method.toLowerCase()),
O.getOrElseW(() => REQUEST_METHOD_LABEL_COLORS.default)
)
}

View File

@@ -25,6 +25,9 @@ export function createHoppApp(el: string | Element, platformDef: PlatformDef) {
const app = createApp(App) const app = createApp(App)
HOPP_MODULES.forEach((mod) => mod.onVueAppInit?.(app))
platformDef.addedHoppModules?.forEach((mod) => mod.onVueAppInit?.(app))
// Some basic work that needs to be done before module inits even // Some basic work that needs to be done before module inits even
initBackendGQLClient() initBackendGQLClient()
initializeApp() initializeApp()
@@ -37,9 +40,6 @@ export function createHoppApp(el: string | Element, platformDef: PlatformDef) {
getService(TestWorkspaceProviderService) getService(TestWorkspaceProviderService)
getService(PersonalWorkspaceProviderService) getService(PersonalWorkspaceProviderService)
HOPP_MODULES.forEach((mod) => mod.onVueAppInit?.(app))
platformDef.addedHoppModules?.forEach((mod) => mod.onVueAppInit?.(app))
app.mount(el) app.mount(el)
console.info( console.info(

View File

@@ -319,6 +319,7 @@ const restCollectionDispatchers = defineDispatchers({
) )
return {} return {}
} }
// We get the index path to the folder itself, // We get the index path to the folder itself,
// we have to find the folder containing the target folder, // we have to find the folder containing the target folder,
// so we pop the last path index // so we pop the last path index

View File

@@ -13,6 +13,7 @@ import { HandleRef } from "./handle"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { Workspace, WorkspaceCollection } from "./workspace" import { Workspace, WorkspaceCollection } from "./workspace"
import { RESTCollectionChildrenView, RootRESTCollectionView } from "./view" import { RESTCollectionChildrenView, RootRESTCollectionView } from "./view"
import { HoppRESTRequest } from "@hoppscotch/data"
export type WorkspaceError<ServiceErr> = export type WorkspaceError<ServiceErr> =
| { type: "SERVICE_ERROR"; error: ServiceErr } | { type: "SERVICE_ERROR"; error: ServiceErr }
@@ -172,7 +173,8 @@ export class NewWorkspaceService extends Service {
public async createRESTChildCollection( public async createRESTChildCollection(
parentCollHandle: HandleRef<WorkspaceCollection>, parentCollHandle: HandleRef<WorkspaceCollection>,
collectionName: string collectionName: string,
path: string
): Promise< ): Promise<
E.Either< E.Either<
WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">,
@@ -193,7 +195,8 @@ export class NewWorkspaceService extends Service {
const result = await provider.createRESTChildCollection( const result = await provider.createRESTChildCollection(
parentCollHandle, parentCollHandle,
collectionName collectionName,
path
) )
if (E.isLeft(result)) { if (E.isLeft(result)) {
@@ -203,6 +206,186 @@ export class NewWorkspaceService extends Service {
return E.right(result.right) return E.right(result.right)
} }
public async createRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
requestName: string,
path: string
): Promise<
E.Either<
WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">,
HandleRef<WorkspaceCollection>
>
> {
if (parentCollHandle.value.type === "invalid") {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" })
}
const provider = this.registeredProviders.get(
parentCollHandle.value.data.providerID
)
if (!provider) {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" })
}
const result = await provider.createRESTRequest(
parentCollHandle,
requestName,
path
)
if (E.isLeft(result)) {
return E.left({ type: "PROVIDER_ERROR", error: result.left })
}
return E.right(result.right)
}
public async removeRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
path: string,
requestIndex: number
): Promise<
E.Either<
WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">,
HandleRef<WorkspaceCollection>
>
> {
if (parentCollHandle.value.type === "invalid") {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" })
}
const provider = this.registeredProviders.get(
parentCollHandle.value.data.providerID
)
if (!provider) {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" })
}
const result = await provider.removeRESTRequest(
parentCollHandle,
path,
requestIndex
)
if (E.isLeft(result)) {
return E.left({ type: "PROVIDER_ERROR", error: result.left })
}
return E.right(result.right)
}
public async selectRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
requestIndex: string,
request: HoppRESTRequest
): Promise<
E.Either<
WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">,
HandleRef<WorkspaceCollection>
>
> {
if (parentCollHandle.value.type === "invalid") {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" })
}
const provider = this.registeredProviders.get(
parentCollHandle.value.data.providerID
)
if (!provider) {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" })
}
const result = await provider.selectRESTRequest(
parentCollHandle,
collPath,
requestIndex,
request
)
if (E.isLeft(result)) {
return E.left({ type: "PROVIDER_ERROR", error: result.left })
}
return E.right(result.right)
}
public async duplicateRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
request: HoppRESTRequest
): Promise<
E.Either<
WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">,
HandleRef<WorkspaceCollection>
>
> {
if (parentCollHandle.value.type === "invalid") {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" })
}
const provider = this.registeredProviders.get(
parentCollHandle.value.data.providerID
)
if (!provider) {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" })
}
const result = await provider.duplicateRESTRequest(
parentCollHandle,
collPath,
request
)
if (E.isLeft(result)) {
return E.left({ type: "PROVIDER_ERROR", error: result.left })
}
return E.right(result.right)
}
public async editRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
requestIndex: number,
request: HoppRESTRequest
): Promise<
E.Either<
WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">,
HandleRef<WorkspaceCollection>
>
> {
if (parentCollHandle.value.type === "invalid") {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" })
}
const provider = this.registeredProviders.get(
parentCollHandle.value.data.providerID
)
if (!provider) {
return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" })
}
const result = await provider.editRESTRequest(
parentCollHandle,
collPath,
requestIndex,
request
)
if (E.isLeft(result)) {
return E.left({ type: "PROVIDER_ERROR", error: result.left })
}
return E.right(result.right)
}
public async getRESTCollectionChildrenView( public async getRESTCollectionChildrenView(
collectionHandle: HandleRef<WorkspaceCollection> collectionHandle: HandleRef<WorkspaceCollection>
): Promise< ): Promise<

View File

@@ -3,6 +3,7 @@ import * as E from "fp-ts/Either"
import { HandleRef } from "./handle" import { HandleRef } from "./handle"
import { Workspace, WorkspaceCollection, WorkspaceDecor } from "./workspace" import { Workspace, WorkspaceCollection, WorkspaceDecor } from "./workspace"
import { RESTCollectionChildrenView, RootRESTCollectionView } from "./view" import { RESTCollectionChildrenView, RootRESTCollectionView } from "./view"
import { HoppRESTRequest } from "@hoppscotch/data"
export interface WorkspaceProvider { export interface WorkspaceProvider {
providerID: string providerID: string
@@ -30,6 +31,34 @@ export interface WorkspaceProvider {
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> ): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>>
createRESTChildCollection( createRESTChildCollection(
parentCollHandle: HandleRef<WorkspaceCollection>, parentCollHandle: HandleRef<WorkspaceCollection>,
collectionName: string collectionName: string,
path: string
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>>
createRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
requestName: string,
path: string
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>>
removeRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
path: string,
requestIndex: number
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>>
selectRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
requestIndex: string,
request: HoppRESTRequest
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>>
duplicateRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
request: HoppRESTRequest
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>>
editRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
requestIndex: number,
request: HoppRESTRequest
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> ): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>>
} }

View File

@@ -1,18 +1,21 @@
import { import { HoppCollection, makeCollection } from "@hoppscotch/data"
HoppCollection,
HoppRESTRequest,
makeCollection,
} from "@hoppscotch/data"
import { Service } from "dioc" import { Service } from "dioc"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { get } from "lodash-es" import { Ref, computed, markRaw, nextTick, ref, shallowRef } from "vue"
import { v4 as uuid } from "uuid"
import { Ref, computed, markRaw, ref, shallowReactive, shallowRef } from "vue"
import PersonalWorkspaceSelector from "~/components/workspace/PersonalWorkspaceSelector.vue" import PersonalWorkspaceSelector from "~/components/workspace/PersonalWorkspaceSelector.vue"
import { useStreamStatic } from "~/composables/stream" import { useStreamStatic } from "~/composables/stream"
import { addRESTCollection, restCollectionStore } from "~/newstore/collections" import {
addRESTCollection,
addRESTFolder,
cascadeParentCollectionForHeaderAuth,
editRESTRequest,
navigateToFolderWithIndexPath,
removeRESTRequest,
restCollectionStore,
saveRESTRequestAs,
} from "~/newstore/collections"
import { platform } from "~/platform" import { platform } from "~/platform"
import { HandleRef } from "~/services/new-workspace/handle" import { HandleRef } from "~/services/new-workspace/handle"
@@ -28,8 +31,15 @@ import {
WorkspaceDecor, WorkspaceDecor,
} from "~/services/new-workspace/workspace" } from "~/services/new-workspace/workspace"
import { cloneDeep } from "lodash-es"
import {
getRequestsByPath,
resolveSaveContextOnRequestReorder,
} from "~/helpers/collection/request"
import { RESTTabService } from "~/services/tab/rest"
import IconUser from "~icons/lucide/user" import IconUser from "~icons/lucide/user"
import { NewWorkspaceService } from ".." import { NewWorkspaceService } from ".."
import { HoppRESTRequest } from "@hoppscotch/data"
export class PersonalWorkspaceProviderService export class PersonalWorkspaceProviderService
extends Service extends Service
@@ -40,6 +50,7 @@ export class PersonalWorkspaceProviderService
public readonly providerID = "PERSONAL_WORKSPACE_PROVIDER" public readonly providerID = "PERSONAL_WORKSPACE_PROVIDER"
private workspaceService = this.bind(NewWorkspaceService) private workspaceService = this.bind(NewWorkspaceService)
private tabs = this.bind(RESTTabService)
public workspaceDecor: Ref<WorkspaceDecor> = ref({ public workspaceDecor: Ref<WorkspaceDecor> = ref({
headerCurrentIcon: IconUser, headerCurrentIcon: IconUser,
@@ -63,77 +74,68 @@ export class PersonalWorkspaceProviderService
this.workspaceService.registerWorkspaceProvider(this) this.workspaceService.registerWorkspaceProvider(this)
} }
private collectionIDMap = shallowReactive( private navigateToFolderWithIndexPath(
new WeakMap<HoppCollection, string>() collections: HoppCollection[],
) indexPaths: number[]
private reqIDMap = shallowReactive(new WeakMap<HoppRESTRequest, string>())
private collectionIDPathMap = shallowReactive(new Map<string, number[]>())
private generatedUUIDs = new Set<string>()
private generateUniqueUUID() {
let id = uuid()
while (this.generatedUUIDs.has(id)) {
id = uuid()
}
this.generatedUUIDs.add(id)
return id
}
private resolvePathFromCollectionID(id: string): number[] | undefined {
return this.collectionIDPathMap.get(id)
}
private resolveCollectionFromCollectionID(
id: string
): HoppCollection | undefined {
const path = this.resolvePathFromCollectionID(id)
if (path === undefined) return
const collPath = path.flatMap((x, i) =>
i === 0 ? [x.toString()] : ["folders", x.toString()]
)
const coll = get(this.restCollectionState.value.state, collPath) as
| HoppCollection
| undefined
return coll
}
private getIssuedInstanceIDForCollection(
coll: HoppCollection,
location: number[]
) { ) {
const id = this.collectionIDMap.has(coll) if (indexPaths.length === 0) return null
? this.collectionIDMap.get(coll)!
: this.generateUniqueUUID()
this.collectionIDPathMap.set(id, location) let target = collections[indexPaths.shift() as number]
this.collectionIDMap.set(coll, id)
return id while (indexPaths.length > 0)
} target = target?.folders[indexPaths.shift() as number]
private getIssuedInstanceIDForRequest(req: HoppRESTRequest) { return target !== undefined ? target : null
const id = this.reqIDMap.get(req) ?? this.generateUniqueUUID()
this.reqIDMap.set(req, id)
return id
} }
public createRESTChildCollection( public createRESTChildCollection(
parentCollHandle: HandleRef<WorkspaceCollection>, parentCollHandle: HandleRef<WorkspaceCollection>,
collectionName: string collectionName: string,
path: string
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> { ): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> {
throw new Error("TODO: Method not implemented.") if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return Promise.resolve(E.left("INVALID_WORKSPACE_HANDLE" as const))
}
return Promise.resolve(
E.right(
computed(() => {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return {
type: "invalid" as const,
reason: "WORKSPACE_INVALIDATED" as const,
}
}
addRESTFolder(collectionName, path)
platform.analytics?.logEvent({
type: "HOPP_CREATE_COLLECTION",
workspaceType: "personal",
isRootCollection: false,
platform: "rest",
})
return {
type: "ok",
data: {
providerID: this.providerID,
workspaceID: parentCollHandle.value.data.workspaceID,
collectionID: parentCollHandle.value.data.collectionID,
name: collectionName,
},
}
})
)
)
} }
public createRESTRootCollection( public createRESTRootCollection(
@@ -157,7 +159,7 @@ export class PersonalWorkspaceProviderService
workspaceHandle.value.data.workspaceID !== "personal" workspaceHandle.value.data.workspaceID !== "personal"
) { ) {
return { return {
type: "invalid", type: "invalid" as const,
reason: "WORKSPACE_INVALIDATED" as const, reason: "WORKSPACE_INVALIDATED" as const,
} }
} }
@@ -196,6 +198,329 @@ export class PersonalWorkspaceProviderService
) )
} }
public createRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
requestName: string,
path: string
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return Promise.resolve(E.left("INVALID_WORKSPACE_HANDLE" as const))
}
return Promise.resolve(
E.right(
computed(() => {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return {
type: "invalid" as const,
reason: "WORKSPACE_INVALIDATED" as const,
}
}
const newRequest = {
...cloneDeep(this.tabs.currentActiveTab.value.document.request),
name: requestName,
}
const insertionIndex = saveRESTRequestAs(path, newRequest)
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
path,
"rest"
)
this.tabs.createNewTab({
request: newRequest,
isDirty: false,
saveContext: {
originLocation: "user-collection",
folderPath: path,
requestIndex: insertionIndex,
},
inheritedProperties: {
auth,
headers,
},
})
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
workspaceType: "personal",
createdNow: true,
platform: "rest",
})
return {
type: "ok",
data: {
providerID: this.providerID,
workspaceID: parentCollHandle.value.data.workspaceID,
collectionID: parentCollHandle.value.data.collectionID,
name: requestName,
},
}
})
)
)
}
public removeRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
path: string,
requestIndex: number
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return Promise.resolve(E.left("INVALID_WORKSPACE_HANDLE" as const))
}
return Promise.resolve(
E.right(
computed(() => {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return {
type: "invalid" as const,
reason: "WORKSPACE_INVALIDATED" as const,
}
}
const possibleTab = this.tabs.getTabRefWithSaveContext({
originLocation: "user-collection",
folderPath: path,
requestIndex,
})
// If there is a tab attached to this request, dissociate its state and mark it dirty
if (possibleTab) {
possibleTab.value.document.saveContext = null
possibleTab.value.document.isDirty = true
}
console.log(
`REST collection store state is `,
restCollectionStore.value.state
)
const requestToRemove = navigateToFolderWithIndexPath(
restCollectionStore.value.state,
path.split("/").map((i) => parseInt(i))
)?.requests[requestIndex]
removeRESTRequest(path, requestIndex, requestToRemove?.id)
// the same function is used to reorder requests since after removing, it's basically doing reorder
resolveSaveContextOnRequestReorder({
lastIndex: requestIndex,
newIndex: -1,
folderPath: path,
length: getRequestsByPath(
this.restCollectionState.value.state,
path
).length,
})
return {
type: "ok",
data: {
providerID: this.providerID,
workspaceID: parentCollHandle.value.data.workspaceID,
collectionID: parentCollHandle.value.data.workspaceID,
name: "" as const,
},
}
})
)
)
}
public selectRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
requestIndex: string,
request: HoppRESTRequest
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return Promise.resolve(E.left("INVALID_WORKSPACE_HANDLE" as const))
}
return Promise.resolve(
E.right(
computed(() => {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return {
type: "invalid" as const,
reason: "WORKSPACE_INVALIDATED" as const,
}
}
// If there is a request with this save context, switch into it
let possibleTab = null
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
collPath,
"rest"
)
possibleTab = this.tabs.getTabRefWithSaveContext({
originLocation: "user-collection",
requestIndex: parseInt(requestIndex),
folderPath: collPath!,
})
if (possibleTab) {
this.tabs.setActiveTab(possibleTab.value.id)
} else {
// If not, open the request in a new tab
this.tabs.createNewTab({
request: cloneDeep(request),
isDirty: false,
saveContext: {
originLocation: "user-collection",
folderPath: collPath!,
requestIndex: parseInt(requestIndex),
},
inheritedProperties: {
auth,
headers,
},
})
}
return {
type: "ok",
data: {
providerID: this.providerID,
workspaceID: parentCollHandle.value.data.workspaceID,
collectionID: parentCollHandle.value.data.workspaceID,
name: "",
},
}
})
)
)
}
public duplicateRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
request: HoppRESTRequest
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return Promise.resolve(E.left("INVALID_WORKSPACE_HANDLE" as const))
}
return Promise.resolve(
E.right(
computed(() => {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return {
type: "invalid" as const,
reason: "WORKSPACE_INVALIDATED" as const,
}
}
saveRESTRequestAs(collPath, request)
return {
type: "ok",
data: {
providerID: this.providerID,
workspaceID: parentCollHandle.value.data.workspaceID,
collectionID: parentCollHandle.value.data.workspaceID,
name: "",
},
}
})
)
)
}
public editRESTRequest(
parentCollHandle: HandleRef<WorkspaceCollection>,
collPath: string,
requestIndex: number,
request: HoppRESTRequest
): Promise<E.Either<unknown, HandleRef<WorkspaceCollection>>> {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return Promise.resolve(E.left("INVALID_WORKSPACE_HANDLE" as const))
}
return Promise.resolve(
E.right(
computed(() => {
if (
parentCollHandle.value.type !== "ok" ||
parentCollHandle.value.data.providerID !== this.providerID ||
parentCollHandle.value.data.workspaceID !== "personal"
) {
return {
type: "invalid" as const,
reason: "WORKSPACE_INVALIDATED" as const,
}
}
const possibleActiveTab = this.tabs.getTabRefWithSaveContext({
originLocation: "user-collection",
requestIndex,
collPath,
})
editRESTRequest(collPath, requestIndex, request)
if (possibleActiveTab) {
possibleActiveTab.value.document.request.name = request.name
nextTick(() => {
possibleActiveTab.value.document.isDirty = false
})
}
return {
type: "ok",
data: {
providerID: this.providerID,
workspaceID: parentCollHandle.value.data.workspaceID,
collectionID: parentCollHandle.value.data.workspaceID,
name: "",
},
}
})
)
)
}
public getCollectionHandle( public getCollectionHandle(
workspaceHandle: HandleRef<Workspace>, workspaceHandle: HandleRef<Workspace>,
collectionID: string collectionID: string
@@ -224,24 +549,13 @@ export class PersonalWorkspaceProviderService
} }
} }
// TODO: The way the IDs are issued, this will make it so we need a view
// before the ID is issued correctly
const coll = this.resolveCollectionFromCollectionID(collectionID)
if (coll === undefined) {
return {
type: "invalid",
reason: "INVALID_COLL_ID" as const,
}
}
return { return {
type: "ok", type: "ok",
data: { data: {
providerID: this.providerID, providerID: this.providerID,
workspaceID: workspaceHandle.value.data.workspaceID, workspaceID: workspaceHandle.value.data.workspaceID,
collectionID, collectionID,
name: coll.name, name: "" as const,
}, },
} }
}) })
@@ -279,45 +593,41 @@ export class PersonalWorkspaceProviderService
mayHaveMoreContent: ref(false), mayHaveMoreContent: ref(false),
content: computed(() => { content: computed(() => {
const path = this.resolvePathFromCollectionID(collectionID) const indexPath = collectionID
const coll = .split("/")
this.resolveCollectionFromCollectionID(collectionID) .map((x) => parseInt(x))
if (coll === undefined || path === undefined) { const item = this.navigateToFolderWithIndexPath(
console.warn("Collection against ID was not resolvable") this.restCollectionState.value.state,
indexPath
)
return [] if (item) {
const collections = item.folders.map((childColl, id) => {
return <RESTCollectionViewItem>{
type: "collection",
value: {
collectionID: `${collectionID}/${id}`,
name: childColl.name,
},
}
})
const requests = item.requests.map((req, id) => {
return <RESTCollectionViewItem>{
type: "request",
value: {
requestID: `${collectionID}/${id}`,
name: req.name,
method: req.method,
request: req,
},
}
})
return [...collections, ...requests]
} }
return []
const collections = coll.folders.map((childColl, i) => {
const id = this.getIssuedInstanceIDForCollection(childColl, [
...path,
i,
])
return <RESTCollectionViewItem>{
type: "collection",
value: {
collectionID: id,
name: coll.name,
},
}
})
const requests = coll.requests.map((req, i) => {
const id = this.getIssuedInstanceIDForRequest(req)
return <RESTCollectionViewItem>{
type: "request",
value: {
requestID: id,
name: req.name,
method: req.method,
},
}
})
return [...collections, ...requests]
}), }),
loadMore() { loadMore() {
return Promise.resolve() return Promise.resolve()
@@ -356,11 +666,9 @@ export class PersonalWorkspaceProviderService
mayHaveMoreContent: ref(false), mayHaveMoreContent: ref(false),
collections: computed(() => { collections: computed(() => {
return this.restCollectionState.value.state.map((coll, i) => { return this.restCollectionState.value.state.map((coll, id) => {
const id = this.getIssuedInstanceIDForCollection(coll, [i])
return { return {
collectionID: id, collectionID: id.toString(),
name: coll.name, name: coll.name,
} }
}) })

View File

@@ -1,3 +1,4 @@
import { HoppRESTRequest } from "@hoppscotch/data"
import { Ref } from "vue" import { Ref } from "vue"
export type RESTCollectionViewCollection = { export type RESTCollectionViewCollection = {
@@ -10,6 +11,7 @@ export type RESTCollectionViewRequest = {
name: string name: string
method: string method: string
request: HoppRESTRequest
} }
export type RESTCollectionViewItem = export type RESTCollectionViewItem =