From c1a8a871d2ea695b7608b166a924e7ae6d3bac20 Mon Sep 17 00:00:00 2001 From: jamesgeorge007 Date: Fri, 9 Feb 2024 13:48:19 +0530 Subject: [PATCH] refactor: save request handle in tabs and remove tabs related logic from personal provider definition --- .../components/collections/SaveRequest.vue | 16 ++- .../src/components/http/Request.vue | 98 +++++++------------ .../components/new-collections/rest/index.vue | 71 +++++++++++--- .../src/helpers/rest/document.ts | 13 +++ .../src/services/new-workspace/index.ts | 6 +- .../src/services/new-workspace/provider.ts | 3 +- .../providers/personal.workspace.ts | 81 +-------------- .../src/services/new-workspace/workspace.ts | 4 +- .../persistence/validation-schemas/index.ts | 7 ++ 9 files changed, 133 insertions(+), 166 deletions(-) diff --git a/packages/hoppscotch-common/src/components/collections/SaveRequest.vue b/packages/hoppscotch-common/src/components/collections/SaveRequest.vue index 0a40acb32..a91c4c40a 100644 --- a/packages/hoppscotch-common/src/components/collections/SaveRequest.vue +++ b/packages/hoppscotch-common/src/components/collections/SaveRequest.vue @@ -253,20 +253,19 @@ const saveRequestAs = async () => { return } - const resultHandle = await workspaceService.createRESTRequest( + const requestHandleResult = await workspaceService.createRESTRequest( collHandle, - updatedRequest.name, - false + updatedRequest ) - if (E.isLeft(resultHandle)) { + if (E.isLeft(requestHandleResult)) { // WORKSPACE_INVALIDATED | INVALID_COLLECTION_HANDLE return } - const result = resultHandle.right + const requestHandle = requestHandleResult.right - if (result.value.type === "invalid") { + if (requestHandle.value.type === "invalid") { // WORKSPACE_INVALIDATED | INVALID_COLLECTION_HANDLE return } @@ -315,9 +314,8 @@ const saveRequestAs = async () => { request: updatedRequest, isDirty: false, saveContext: { - originLocation: "user-collection", - folderPath: picked.value.folderPath, - requestIndex: picked.value.requestIndex, + originLocation: "workspace-user-collection", + requestHandle, }, } diff --git a/packages/hoppscotch-common/src/components/http/Request.vue b/packages/hoppscotch-common/src/components/http/Request.vue index 8c8dd7770..8ef60e76c 100644 --- a/packages/hoppscotch-common/src/components/http/Request.vue +++ b/packages/hoppscotch-common/src/components/http/Request.vue @@ -236,15 +236,28 @@ import { useI18n } from "@composables/i18n" import { useSetting } from "@composables/settings" import { useReadonlyStream, useStreamSubscriber } from "@composables/stream" import { useToast } from "@composables/toast" +import { HoppRESTRequest } from "@hoppscotch/data" import { useVModel } from "@vueuse/core" +import { useService } from "dioc/vue" import * as E from "fp-ts/Either" -import { Ref, computed, ref, onUnmounted } from "vue" +import { Ref, computed, onUnmounted, ref } from "vue" +import { runRESTRequest$ } from "~/helpers/RequestRunner" import { defineActionHandler, invokeAction } from "~/helpers/actions" import { runMutation } from "~/helpers/backend/GQLClient" import { UpdateRequestDocument } from "~/helpers/backend/graphql" import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils" -import { runRESTRequest$ } from "~/helpers/RequestRunner" +import { getDefaultRESTRequest } from "~/helpers/rest/default" +import { HoppRESTDocument } from "~/helpers/rest/document" +import { getMethodLabelColor } from "~/helpers/rest/labelColoring" import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse" +import { RESTHistoryEntry, restHistory$ } from "~/newstore/history" +import { platform } from "~/platform" +import { InspectionService } from "~/services/inspection" +import { InterceptorService } from "~/services/interceptor.service" +import { NewWorkspaceService } from "~/services/new-workspace" +import { HoppTab } from "~/services/tab" +import { RESTTabService } from "~/services/tab/rest" +import { WorkspaceService } from "~/services/workspace.service" import IconChevronDown from "~icons/lucide/chevron-down" import IconCode2 from "~icons/lucide/code-2" import IconFileCode from "~icons/lucide/file-code" @@ -252,19 +265,6 @@ import IconFolderPlus from "~icons/lucide/folder-plus" import IconRotateCCW from "~icons/lucide/rotate-ccw" import IconSave from "~icons/lucide/save" import IconShare2 from "~icons/lucide/share-2" -import { getDefaultRESTRequest } from "~/helpers/rest/default" -import { RESTHistoryEntry, restHistory$ } from "~/newstore/history" -import { platform } from "~/platform" -import { HoppRESTRequest } from "@hoppscotch/data" -import { useService } from "dioc/vue" -import { InspectionService } from "~/services/inspection" -import { InterceptorService } from "~/services/interceptor.service" -import { HoppTab } from "~/services/tab" -import { HoppRESTDocument } from "~/helpers/rest/document" -import { RESTTabService } from "~/services/tab/rest" -import { getMethodLabelColor } from "~/helpers/rest/labelColoring" -import { WorkspaceService } from "~/services/workspace.service" -import { NewWorkspaceService } from "~/services/new-workspace" const t = useI18n() const interceptorService = useService(InterceptorService) @@ -508,80 +508,58 @@ const cycleDownMethod = () => { } const saveRequest = async () => { - const saveCtx = tab.value.document.saveContext + const { saveContext } = tab.value.document - if (!saveCtx) { + if (!saveContext) { showSaveRequestModal.value = true return } - if (saveCtx.originLocation === "user-collection") { + + if (saveContext.originLocation === "workspace-user-collection") { const updatedRequest = tab.value.document.request - if (!newWorkspaceService.activeWorkspaceHandle.value) { + if ( + !newWorkspaceService.activeWorkspaceHandle.value || + !saveContext.requestHandle + ) { return } - const collHandleResult = await newWorkspaceService.getCollectionHandle( - newWorkspaceService.activeWorkspaceHandle.value, - saveCtx.folderPath - ) + const { requestHandle } = saveContext - if (E.isLeft(collHandleResult)) { - // INVALID_WORKSPACE_HANDLE + if (!requestHandle.value) { return } - const collHandle = collHandleResult.right - - if (collHandle.value.type === "invalid") { - // WORKSPACE_INVALIDATED | INVALID_COLLECTION_HANDLE - return - } - - const requestHandleResult = await newWorkspaceService.getRequestHandle( - newWorkspaceService.activeWorkspaceHandle.value, - `${saveCtx.folderPath}/${saveCtx.requestIndex.toString()}` - ) - - if (E.isLeft(requestHandleResult)) { - // INVALID_REQUEST_HANDLE - return - } - - const requestHandle = requestHandleResult.right - if (requestHandle.value.type === "invalid") { - // WORKSPACE_INVALIDATED | INVALID_REQUEST_HANDLE + showSaveRequestModal.value = true return } - const updatedRequestResult = await newWorkspaceService.updateRESTRequest( + const updateRequestResult = await newWorkspaceService.updateRESTRequest( requestHandle, updatedRequest ) - if (E.isLeft(updatedRequestResult)) { + if (E.isLeft(updateRequestResult)) { // INVALID_REQUEST_HANDLE - return - } + showSaveRequestModal.value = true - const resultHandle = updatedRequestResult.right - - if (resultHandle.value.type === "invalid") { - // REQUEST_INVALIDATED | REQUEST_PATH_NOT_FOUND - - if (resultHandle.value.reason === "REQUEST_PATH_NOT_FOUND") { - // REQUEST_PATH_NOT_FOUND - tab.value.document.saveContext = undefined - await saveRequest() + if (!tab.value.document.isDirty) { + tab.value.document.isDirty = true } return } tab.value.document.isDirty = false + tab.value.document.saveContext = { + ...saveContext, + requestHandle, + } + toast.success(`${t("request.saved")}`) - } else if (saveCtx.originLocation === "team-collection") { + } else if (saveContext.originLocation === "team-collection") { const req = tab.value.document.request // TODO: handle error case (NOTE: overwriteRequestTeams is async) @@ -594,7 +572,7 @@ const saveRequest = async () => { }) runMutation(UpdateRequestDocument, { - requestID: saveCtx.requestID, + requestID: saveContext.requestID, data: { title: req.name, request: JSON.stringify(req), diff --git a/packages/hoppscotch-common/src/components/new-collections/rest/index.vue b/packages/hoppscotch-common/src/components/new-collections/rest/index.vue index 218950504..fd5c8eb40 100644 --- a/packages/hoppscotch-common/src/components/new-collections/rest/index.vue +++ b/packages/hoppscotch-common/src/components/new-collections/rest/index.vue @@ -151,6 +151,10 @@ import { TeamCollection } from "~/helpers/backend/graphql" import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties" import { useReadonlyStream } from "~/composables/stream" import { updateInheritedPropertiesForAffectedRequests } from "~/helpers/collection/collection" +import { + resolveSaveContextOnRequestReorder, + getRequestsByPath, +} from "~/helpers/collection/request" const t = useI18n() const toast = useToast() @@ -344,22 +348,46 @@ const onAddRequest = async (requestName: string) => { return } - const result = await workspaceService.createRESTRequest( + const newRequest = { + ...cloneDeep(tabs.currentActiveTab.value.document.request), + name: requestName, + } + + const requestHandleResult = await workspaceService.createRESTRequest( collectionHandle, - requestName, - true + newRequest ) - if (E.isLeft(result)) { + if (E.isLeft(requestHandleResult)) { // INVALID_COLLECTION_HANDLE return } - if (result.right.value.type === "invalid") { + const requestHandle = requestHandleResult.right + + if (requestHandle.value.type === "invalid") { // COLLECTION_INVALIDATED return } + const { auth, headers } = cascadeParentCollectionForHeaderAuth( + requestHandle.value.data.collectionID, + "rest" + ) + + tabs.createNewTab({ + request: newRequest, + isDirty: false, + saveContext: { + originLocation: "workspace-user-collection", + requestHandle, + }, + inheritedProperties: { + auth, + headers, + }, + }) + displayModalAddRequest(false) } @@ -612,6 +640,28 @@ const onRemoveRequest = async () => { return } + const possibleTab = tabs.getTabRefWithSaveContext({ + originLocation: "workspace-user-collection", + requestHandle, + }) + + // 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 + } + + const { collectionID, requestID } = requestHandle.value.data + const requestIndex = parseInt(requestID.split("/").slice(-1)[0]) + + // The same function is used to reorder requests since after removing, it's basically doing reorder + resolveSaveContextOnRequestReorder({ + lastIndex: requestIndex, + newIndex: -1, + folderPath: collectionID, + length: getRequestsByPath(restCollectionState.value, collectionID).length, + }) + toast.success(t("state.deleted")) displayConfirmModal(false) } @@ -636,7 +686,6 @@ const selectRequest = async (requestIndexPath: string) => { return } - const requestIndex = parseInt(requestIndexPath.split("/").slice(-1)[0]) const request = requestHandle.value.data.request as HoppRESTRequest // If there is a request with this save context, switch into it @@ -647,9 +696,8 @@ const selectRequest = async (requestIndexPath: string) => { "rest" ) possibleTab = tabs.getTabRefWithSaveContext({ - originLocation: "user-collection", - requestIndex, - folderPath: collectionIndexPath, + originLocation: "workspace-user-collection", + requestHandle, }) if (possibleTab) { tabs.setActiveTab(possibleTab.value.id) @@ -659,9 +707,8 @@ const selectRequest = async (requestIndexPath: string) => { request: cloneDeep(request), isDirty: false, saveContext: { - originLocation: "user-collection", - folderPath: collectionIndexPath, - requestIndex, + originLocation: "workspace-user-collection", + requestHandle, }, inheritedProperties: { auth, diff --git a/packages/hoppscotch-common/src/helpers/rest/document.ts b/packages/hoppscotch-common/src/helpers/rest/document.ts index afc8e4ff4..97628e73b 100644 --- a/packages/hoppscotch-common/src/helpers/rest/document.ts +++ b/packages/hoppscotch-common/src/helpers/rest/document.ts @@ -3,8 +3,21 @@ import { HoppRESTResponse } from "../types/HoppRESTResponse" import { HoppTestResult } from "../types/HoppTestResult" import { RESTOptionTabs } from "~/components/http/RequestOptions.vue" import { HoppInheritedProperty } from "../types/HoppInheritedProperties" +import { HandleRef } from "~/services/new-workspace/handle" +import { WorkspaceRequest } from "~/services/new-workspace/workspace" export type HoppRESTSaveContext = + | { + /** + * The origin source of the request + */ + // TODO: Make this `user-collection` after porting all usages + originLocation: "workspace-user-collection" + /** + * Handle to a request in the workspace + */ + requestHandle: HandleRef + } | { /** * The origin source of the request diff --git a/packages/hoppscotch-common/src/services/new-workspace/index.ts b/packages/hoppscotch-common/src/services/new-workspace/index.ts index 02d30b440..e73bda5e5 100644 --- a/packages/hoppscotch-common/src/services/new-workspace/index.ts +++ b/packages/hoppscotch-common/src/services/new-workspace/index.ts @@ -331,8 +331,7 @@ export class NewWorkspaceService extends Service { public async createRESTRequest( parentCollectionHandle: HandleRef, - requestName: string, - openInNewTab: boolean + newRequest: HoppRESTRequest ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, @@ -353,8 +352,7 @@ export class NewWorkspaceService extends Service { const result = await provider.createRESTRequest( parentCollectionHandle, - requestName, - openInNewTab + newRequest ) if (E.isLeft(result)) { diff --git a/packages/hoppscotch-common/src/services/new-workspace/provider.ts b/packages/hoppscotch-common/src/services/new-workspace/provider.ts index 34970ffe9..62b44f917 100644 --- a/packages/hoppscotch-common/src/services/new-workspace/provider.ts +++ b/packages/hoppscotch-common/src/services/new-workspace/provider.ts @@ -58,8 +58,7 @@ export interface WorkspaceProvider { ): Promise>> createRESTRequest( parentCollectionHandle: HandleRef, - requestName: string, - openInNewTab: boolean + request: HoppRESTRequest ): Promise>> updateRESTRequest( requestHandle: HandleRef, diff --git a/packages/hoppscotch-common/src/services/new-workspace/providers/personal.workspace.ts b/packages/hoppscotch-common/src/services/new-workspace/providers/personal.workspace.ts index 4cf503ed8..d94531829 100644 --- a/packages/hoppscotch-common/src/services/new-workspace/providers/personal.workspace.ts +++ b/packages/hoppscotch-common/src/services/new-workspace/providers/personal.workspace.ts @@ -9,7 +9,6 @@ import { useStreamStatic } from "~/composables/stream" import { addRESTCollection, addRESTFolder, - cascadeParentCollectionForHeaderAuth, editRESTCollection, editRESTFolder, editRESTRequest, @@ -36,12 +35,6 @@ import { WorkspaceRequest, } 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 { NewWorkspaceService } from ".." import { HoppRESTRequest } from "@hoppscotch/data" @@ -60,7 +53,6 @@ export class PersonalWorkspaceProviderService public readonly providerID = "PERSONAL_WORKSPACE_PROVIDER" private workspaceService = this.bind(NewWorkspaceService) - private tabs = this.bind(RESTTabService) public workspaceDecor: Ref = ref({ headerCurrentIcon: IconUser, @@ -380,8 +372,7 @@ export class PersonalWorkspaceProviderService public createRESTRequest( parentCollHandle: HandleRef, - requestName: string, - openInNewTab: boolean + newRequest: HoppRESTRequest ): Promise>> { if ( parentCollHandle.value.type !== "ok" || @@ -408,34 +399,8 @@ export class PersonalWorkspaceProviderService const { collectionID, providerID, workspaceID } = parentCollHandle.value.data - const newRequest = { - ...cloneDeep(this.tabs.currentActiveTab.value.document.request), - name: requestName, - } - const insertionIndex = saveRESTRequestAs(collectionID, newRequest) - if (openInNewTab) { - const { auth, headers } = cascadeParentCollectionForHeaderAuth( - collectionID, - "rest" - ) - - this.tabs.createNewTab({ - request: newRequest, - isDirty: false, - saveContext: { - originLocation: "user-collection", - folderPath: collectionID, - requestIndex: insertionIndex, - }, - inheritedProperties: { - auth, - headers, - }, - }) - } - platform.analytics?.logEvent({ type: "HOPP_SAVE_REQUEST", workspaceType: "personal", @@ -488,18 +453,6 @@ export class PersonalWorkspaceProviderService const { collectionID, requestID } = requestHandle.value.data const requestIndex = parseInt(requestID.split("/").slice(-1)[0]) - const possibleTab = this.tabs.getTabRefWithSaveContext({ - originLocation: "user-collection", - folderPath: collectionID, - 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 - } - const requestToRemove = navigateToFolderWithIndexPath( restCollectionStore.value.state, collectionID.split("/").map((id) => parseInt(id)) @@ -507,17 +460,6 @@ export class PersonalWorkspaceProviderService removeRESTRequest(collectionID, 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: collectionID, - length: getRequestsByPath( - this.restCollectionState.value.state, - collectionID - ).length, - }) - return { type: "ok", data: true, @@ -617,14 +559,7 @@ export class PersonalWorkspaceProviderService const collection = navigateToFolderWithIndexPath( this.restCollectionState.value.state, collectionID.split("/").map((x) => parseInt(x)) - ) - - if (!collection) { - return { - type: "invalid" as const, - reason: "INVALID_COLLECTION_HANDLE" as const, - } - } + ) as HoppCollection const { providerID, workspaceID } = workspaceHandle.value.data @@ -634,7 +569,7 @@ export class PersonalWorkspaceProviderService providerID, workspaceID, collectionID, - name: collection.name, + name: collection?.name, }, } }) @@ -696,15 +631,7 @@ export class PersonalWorkspaceProviderService ) // Grab the request with it's index - const request = (collection?.requests[requestIndex] ?? - null) as HoppRESTRequest | null - - if (!request) { - return { - type: "invalid" as const, - reason: "INVALID_REQUEST_HANDLE" as const, - } - } + const request = collection?.requests[requestIndex] as HoppRESTRequest return { type: "ok", diff --git a/packages/hoppscotch-common/src/services/new-workspace/workspace.ts b/packages/hoppscotch-common/src/services/new-workspace/workspace.ts index 6fca2694a..d14c7254b 100644 --- a/packages/hoppscotch-common/src/services/new-workspace/workspace.ts +++ b/packages/hoppscotch-common/src/services/new-workspace/workspace.ts @@ -1,4 +1,4 @@ -import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data" +import { HoppRESTRequest } from "@hoppscotch/data" import { Component } from "vue" export type Workspace = { @@ -22,7 +22,7 @@ export type WorkspaceRequest = { collectionID: string requestID: string - request: HoppRESTRequest | null + request: HoppRESTRequest } export type WorkspaceDecor = { diff --git a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts index 5efdf7655..05a95c8b3 100644 --- a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts +++ b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts @@ -492,6 +492,13 @@ const HoppRESTResponseSchema = z.discriminatedUnion("type", [ const HoppRESTSaveContextSchema = z.nullable( z.discriminatedUnion("originLocation", [ + z + .object({ + originLocation: z.literal("workspace-user-collection"), + // TODO: Specify the correct shape + requestHandle: z.any(), + }) + .strict(), z .object({ originLocation: z.literal("user-collection"),