import { Service } from "dioc" import { Component, Ref, computed, markRaw, shallowReactive, shallowRef, watch, } from "vue" import { WorkspaceProvider } from "./provider" import { HandleRef } from "./handle" import * as E from "fp-ts/Either" import { Workspace, WorkspaceCollection, WorkspaceRequest } from "./workspace" import { RESTCollectionChildrenView, RESTCollectionLevelAuthHeadersView, RootRESTCollectionView, } from "./view" import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data" export type WorkspaceError = | { type: "SERVICE_ERROR"; error: ServiceErr } | { type: "PROVIDER_ERROR"; error: unknown } export class NewWorkspaceService extends Service { public static readonly ID = "NEW_WORKSPACE_SERVICE" private registeredProviders = shallowReactive( new Map() ) public activeWorkspaceHandle: Ref | undefined> = shallowRef() public activeWorkspaceDecor = computed(() => { if (this.activeWorkspaceHandle.value?.value.type !== "ok") { return undefined } return this.registeredProviders.get( this.activeWorkspaceHandle.value.value.data.providerID )!.workspaceDecor }) public workspaceSelectorComponents = computed(() => { const items: Component[] = [] const sortedProviders = Array.from(this.registeredProviders.values()).sort( (a, b) => (b.workspaceDecor?.value.workspaceSelectorPriority ?? 0) - (a.workspaceDecor?.value.workspaceSelectorPriority ?? 0) ) for (const workspace of sortedProviders) { if (workspace.workspaceDecor?.value?.workspaceSelectorComponent) { items.push(workspace.workspaceDecor.value.workspaceSelectorComponent) } } return items }) constructor() { super() // Watch for situations where the handle is invalidated // so the active workspace handle definition can be invalidated watch( () => { return this.activeWorkspaceHandle.value ? [ this.activeWorkspaceHandle.value, this.activeWorkspaceHandle.value.value, ] : [this.activeWorkspaceHandle.value] }, () => { if (!this.activeWorkspaceHandle.value) return if (this.activeWorkspaceHandle.value.value.type === "invalid") { this.activeWorkspaceHandle.value = undefined } }, { deep: true } ) } public async getWorkspaceHandle( providerID: string, workspaceID: string ): Promise< E.Either, HandleRef> > { const provider = this.registeredProviders.get(providerID) if (!provider) { return Promise.resolve( E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" as const }) ) } const handleResult = await provider.getWorkspaceHandle(workspaceID) if (E.isLeft(handleResult)) { return E.left({ type: "PROVIDER_ERROR", error: handleResult.left }) } return E.right(handleResult.right) } public async getCollectionHandle( workspaceHandle: HandleRef, collectionID: string ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, HandleRef > > { 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.getCollectionHandle( workspaceHandle, collectionID ) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(result.right) } public async getRequestHandle( workspaceHandle: HandleRef, requestID: string ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, HandleRef > > { 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.getRequestHandle(workspaceHandle, requestID) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(result.right) } public async createRESTRootCollection( workspaceHandle: HandleRef, newCollection: Partial ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, HandleRef > > { 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.createRESTRootCollection( workspaceHandle, newCollection ) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(result.right) } public async createRESTChildCollection( parentCollectionHandle: HandleRef, newChildCollection: Partial ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, HandleRef > > { if (parentCollectionHandle.value.type === "invalid") { return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" }) } const provider = this.registeredProviders.get( parentCollectionHandle.value.data.providerID ) if (!provider) { return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" }) } const result = await provider.createRESTChildCollection( parentCollectionHandle, newChildCollection ) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(result.right) } public async updateRESTCollection( collectionHandle: HandleRef, updatedCollection: Partial ): Promise< E.Either, void> > { if (collectionHandle.value.type === "invalid") { return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" }) } const provider = this.registeredProviders.get( collectionHandle.value.data.providerID ) if (!provider) { return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" }) } const result = await provider.updateRESTCollection( collectionHandle, updatedCollection ) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(undefined) } public async removeRESTCollection( collectionHandle: HandleRef ): Promise< E.Either, void> > { if (collectionHandle.value.type === "invalid") { return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" }) } const provider = this.registeredProviders.get( collectionHandle.value.data.providerID ) if (!provider) { return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" }) } const result = await provider.removeRESTCollection(collectionHandle) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(undefined) } public async createRESTRequest( parentCollectionHandle: HandleRef, newRequest: HoppRESTRequest ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, HandleRef > > { if (parentCollectionHandle.value.type === "invalid") { return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" }) } const provider = this.registeredProviders.get( parentCollectionHandle.value.data.providerID ) if (!provider) { return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" }) } const result = await provider.createRESTRequest( parentCollectionHandle, newRequest ) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(result.right) } public async removeRESTRequest( requestHandle: HandleRef ): Promise< E.Either, void> > { if (requestHandle.value.type === "invalid") { return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" }) } const provider = this.registeredProviders.get( requestHandle.value.data.providerID ) if (!provider) { return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" }) } const result = await provider.removeRESTRequest(requestHandle) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(undefined) } public async updateRESTRequest( requestHandle: HandleRef, updatedRequest: Partial ): Promise< E.Either, void> > { if (requestHandle.value.type === "invalid") { return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" }) } const provider = this.registeredProviders.get( requestHandle.value.data.providerID ) if (!provider) { return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" }) } const result = await provider.updateRESTRequest( requestHandle, updatedRequest ) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(result.right) } public async getRESTCollectionChildrenView( collectionHandle: HandleRef ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, HandleRef > > { if (collectionHandle.value.type === "invalid") { return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" }) } const provider = this.registeredProviders.get( collectionHandle.value.data.providerID ) if (!provider) { return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" }) } const result = await provider.getRESTCollectionChildrenView(collectionHandle) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(result.right) } public async getRESTRootCollectionView( workspaceHandle: HandleRef ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, HandleRef > > { 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.getRESTRootCollectionView(workspaceHandle) if (E.isLeft(result)) { return E.left({ type: "PROVIDER_ERROR", error: result.left }) } return E.right(result.right) } public async getRESTCollectionLevelAuthHeadersView( collectionHandle: HandleRef ): Promise< E.Either< WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, HandleRef > > { if (collectionHandle.value.type === "invalid") { return E.left({ type: "SERVICE_ERROR", error: "INVALID_HANDLE" }) } const provider = this.registeredProviders.get( collectionHandle.value.data.providerID ) if (!provider) { return E.left({ type: "SERVICE_ERROR", error: "INVALID_PROVIDER" }) } const result = await provider.getRESTCollectionLevelAuthHeadersView(collectionHandle) 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( "Ignoring attempt to re-register workspace provider that is already existing:", provider ) return } this.registeredProviders.set(provider.providerID, markRaw(provider)) } }