Browse Source

refactor: view based implementation for search in personal workspace

jamesgeorge007 1 year ago
parent
commit
9a63f78792

+ 34 - 0
packages/hoppscotch-common/src/services/new-workspace/index.ts

@@ -15,6 +15,7 @@ import { Workspace, WorkspaceCollection, WorkspaceRequest } from "./workspace"
 import {
   RESTCollectionChildrenView,
   RESTCollectionLevelAuthHeadersView,
+  RESTSearchResultsView,
   RootRESTCollectionView,
 } from "./view"
 import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"
@@ -687,6 +688,39 @@ export class NewWorkspaceService extends Service {
     return E.right(result.right)
   }
 
+  public async getRESTSearchResultsView(
+    workspaceHandle: HandleRef<Workspace>,
+    searchQuery: Ref<string>
+  ): Promise<
+    E.Either<
+      WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">,
+      HandleRef<RESTSearchResultsView>
+    >
+  > {
+    if (workspaceHandle.value.type === "invalid") {
+      return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" })
+    }
+
+    const provider = this.registeredProviders.get(
+      workspaceHandle.value.data.providerID
+    )
+
+    if (!provider) {
+      return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" })
+    }
+
+    const result = await provider.getRESTSearchResultsView(
+      workspaceHandle,
+      searchQuery
+    )
+
+    if (E.isLeft(result)) {
+      return E.left({ type: "PROVIDER_ERROR", error: result.left })
+    }
+
+    return E.right(result.right)
+  }
+
   public registerWorkspaceProvider(provider: WorkspaceProvider) {
     if (this.registeredProviders.has(provider.providerID)) {
       console.warn(

+ 5 - 0
packages/hoppscotch-common/src/services/new-workspace/provider.ts

@@ -11,6 +11,7 @@ import {
   RESTCollectionLevelAuthHeadersView,
   RESTCollectionChildrenView,
   RootRESTCollectionView,
+  RESTSearchResultsView,
 } from "./view"
 import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"
 
@@ -40,6 +41,10 @@ export interface WorkspaceProvider {
   getRESTCollectionLevelAuthHeadersView(
     collectionHandle: HandleRef<WorkspaceCollection>
   ): Promise<E.Either<unknown, HandleRef<RESTCollectionLevelAuthHeadersView>>>
+  getRESTSearchResultsView(
+    workspaceHandle: HandleRef<Workspace>,
+    searchQuery: Ref<string>
+  ): Promise<E.Either<unknown, HandleRef<RESTSearchResultsView>>>
 
   createRESTRootCollection(
     workspaceHandle: HandleRef<Workspace>,

+ 172 - 0
packages/hoppscotch-common/src/services/new-workspace/providers/personal.workspace.ts

@@ -37,7 +37,10 @@ import { WorkspaceProvider } from "~/services/new-workspace/provider"
 import {
   RESTCollectionChildrenView,
   RESTCollectionLevelAuthHeadersView,
+  RESTCollectionViewCollection,
   RESTCollectionViewItem,
+  RESTCollectionViewRequest,
+  RESTSearchResultsView,
   RootRESTCollectionView,
 } from "~/services/new-workspace/view"
 import {
@@ -766,6 +769,7 @@ export class PersonalWorkspaceProviderService
                   })
 
                   const requests = item.requests.map((req, id) => {
+                    // TODO: Replace `parentCollectionID` with `collectionID`
                     return <RESTCollectionViewItem>{
                       type: "request",
                       value: {
@@ -945,6 +949,174 @@ export class PersonalWorkspaceProviderService
     )
   }
 
+  public getRESTSearchResultsView(
+    workspaceHandle: HandleRef<Workspace>,
+    searchQuery: Ref<string>
+  ): Promise<E.Either<never, HandleRef<RESTSearchResultsView>>> {
+    return Promise.resolve(
+      E.right(
+        computed(() => {
+          if (
+            workspaceHandle.value.type === "invalid" ||
+            workspaceHandle.value.data.providerID !== this.providerID ||
+            workspaceHandle.value.data.workspaceID !== "personal"
+          ) {
+            return {
+              type: "invalid" as const,
+              reason: "INVALID_WORKSPACE_HANDLE" as const,
+            }
+          }
+
+          if (!searchQuery.value) {
+            return markRaw({
+              type: "ok" as const,
+              data: {
+                providerID: this.providerID,
+                workspaceID: workspaceHandle.value.data.workspaceID,
+
+                loading: ref(false),
+
+                results: computed(() => {
+                  return this.restCollectionState.value.state.map(
+                    (coll, id) => {
+                      return <RESTCollectionViewItem>{
+                        type: "collection",
+                        value: {
+                          collectionID: id.toString(),
+                          isLastItem:
+                            id ===
+                            this.restCollectionState.value.state.length - 1,
+                          name: coll.name,
+                          parentCollectionID: null,
+                        },
+                      }
+                    }
+                  )
+                }),
+              },
+            })
+          }
+
+          return markRaw({
+            type: "ok" as const,
+            data: {
+              providerID: this.providerID,
+              workspaceID: workspaceHandle.value.data.workspaceID,
+
+              loading: ref(false),
+
+              results: computed(() => {
+                const filterText = searchQuery.value.toLowerCase()
+                const filteredCollections: RESTCollectionViewItem[] = []
+
+                const isMatch = (text: string) =>
+                  text.toLowerCase().includes(filterText)
+
+                for (const collection of this.restCollectionState.value.state) {
+                  const filteredRequests: Extract<
+                    RESTCollectionViewItem,
+                    { type: "request" }
+                  >[] = []
+
+                  const filteredFolders: Extract<
+                    RESTCollectionViewItem,
+                    { type: "collection" }
+                  >[] = []
+
+                  collection.requests.forEach((request, requestID) => {
+                    if (isMatch(request.name)) {
+                      filteredRequests.push({
+                        type: "request",
+                        value: {
+                          collectionID: collection.id!,
+                          isLastItem:
+                            collection.requests?.length > 1
+                              ? requestID === collection.requests.length - 1
+                              : false,
+                          // TODO: Replace `parentCollectionID` with `collectionID`
+                          parentCollectionID: collection.id!,
+                          requestID: requestID.toString(),
+                          request: request as HoppRESTRequest,
+                        },
+                      })
+                    }
+                  })
+
+                  collection.folders.forEach(
+                    (childCollection, childCollectionID) => {
+                      if (isMatch(childCollection.name)) {
+                        filteredFolders.push({
+                          type: "collection",
+                          value: {
+                            collectionID: `${collection.id}/${childCollectionID}`,
+                            isLastItem:
+                              collection.folders?.length > 1
+                                ? childCollectionID ===
+                                  collection.folders.length - 1
+                                : false,
+                            name: childCollection.name,
+                            parentCollectionID: collection.id!,
+                          },
+                        })
+                      }
+
+                      const filteredFolderRequests: Extract<
+                        RESTCollectionViewItem,
+                        { type: "request" }
+                      >[] = ([] = [])
+
+                      childCollection.requests.forEach(
+                        (request: HoppRESTRequest, requestID: number) => {
+                          if (isMatch(request.name))
+                            filteredFolderRequests.push({
+                              type: "request",
+                              value: {
+                                collectionID: childCollection.id!,
+                                isLastItem:
+                                  childCollection.requests?.length > 1
+                                    ? requestID ===
+                                      childCollection.requests.length - 1
+                                    : false,
+                                // TODO: Replace `parentCollectionID` with `collectionID`
+                                parentCollectionID: childCollection.id!,
+                                requestID: requestID.toString(),
+                                request,
+                              },
+                            })
+                        }
+                      )
+
+                      if (filteredFolderRequests.length > 0) {
+                        const filteredFolder = Object.assign(
+                          {},
+                          childCollection
+                        )
+                        filteredFolder.requests = filteredFolderRequests
+                        filteredFolders.push(filteredFolder)
+                      }
+                    }
+                  )
+
+                  if (
+                    filteredRequests.length + filteredFolders.length > 0 ||
+                    isMatch(collection.name)
+                  ) {
+                    filteredCollections.push(
+                      ...filteredFolders,
+                      ...filteredRequests
+                    )
+                  }
+                }
+
+                return filteredCollections
+              }),
+            },
+          })
+        })
+      )
+    )
+  }
+
   public getWorkspaceHandle(
     workspaceID: string
   ): Promise<E.Either<unknown, HandleRef<Workspace>>> {

+ 9 - 0
packages/hoppscotch-common/src/services/new-workspace/view.ts

@@ -46,3 +46,12 @@ export interface RESTCollectionChildrenView {
 
   content: Ref<RESTCollectionViewItem[]>
 }
+
+export interface RESTSearchResultsView {
+  providerID: string
+  workspaceID: string
+
+  loading: Ref<boolean>
+
+  results: Ref<RESTCollectionViewItem[]>
+}