From c0e3ff49b3a0f21cd765babee633953c77e3749a Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Wed, 29 Oct 2025 16:55:02 +0600 Subject: [PATCH] fix (common): address mock server issues and improve the UI (#5517) Co-authored-by: nivedin Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> Co-authored-by: mirarifhasan --- .../src/mock-server/mock-server.model.ts | 6 +- .../src/mock-server/mock-server.resolver.ts | 3 +- .../mock-server/mock-server.service.spec.ts | 17 +- .../src/mock-server/mock-server.service.ts | 24 +- packages/hoppscotch-common/locales/en.json | 4 + .../hoppscotch-common/src/components.d.ts | 3 + .../src/components/collections/Collection.vue | 19 +- .../components/collections/ImportExport.vue | 92 +++++++- .../components/collections/SaveRequest.vue | 2 +- .../collections/graphql/ImportExport.vue | 90 +++++++- .../src/components/collections/index.vue | 9 +- .../src/components/http/Request.vue | 2 +- .../src/components/http/Sidebar.vue | 10 + .../mockServer/CreateMockServer.vue | 148 ++++++------- .../components/mockServer/EditMockServer.vue | 139 +++++++++--- .../src/components/mockServer/LogSection.vue | 114 ++++++++++ .../mockServer/MockServerDashboard.vue | 74 ++----- .../components/mockServer/MockServerLogs.vue | 205 +++++++++++------- .../src/composables/mockServerWorkspace.ts | 51 +++++ .../ImportUserCollectionsFromJSON.graphql | 11 + .../queries/GetGQLRootUserCollections.graphql | 34 +++ .../queries/GetUserRootCollections.graphql | 34 +++ .../helpers/backend/mutations/MockServer.ts | 57 ++++- .../backend/mutations/UserCollection.ts | 203 +++++++++++++++++ .../src/newstore/mockServers.ts | 17 +- .../src/newstore/settings.ts | 2 + .../hoppscotch-common/src/pages/settings.vue | 13 +- .../persistence/validation-schemas/index.ts | 1 + .../collections/collections.platform.ts | 2 - .../src/platform/collections/desktop/index.ts | 14 +- .../src/platform/collections/web/index.ts | 14 +- packages/hoppscotch-sh-admin/locales/en.json | 5 +- .../components/settings/MockServerConfig.vue | 31 +-- .../src/composables/useConfigHandler.ts | 2 - .../src/helpers/configs.ts | 5 - 35 files changed, 1136 insertions(+), 321 deletions(-) create mode 100644 packages/hoppscotch-common/src/components/mockServer/LogSection.vue create mode 100644 packages/hoppscotch-common/src/composables/mockServerWorkspace.ts create mode 100644 packages/hoppscotch-common/src/helpers/backend/gql/mutations/ImportUserCollectionsFromJSON.graphql create mode 100644 packages/hoppscotch-common/src/helpers/backend/gql/queries/GetGQLRootUserCollections.graphql create mode 100644 packages/hoppscotch-common/src/helpers/backend/gql/queries/GetUserRootCollections.graphql create mode 100644 packages/hoppscotch-common/src/helpers/backend/mutations/UserCollection.ts diff --git a/packages/hoppscotch-backend/src/mock-server/mock-server.model.ts b/packages/hoppscotch-backend/src/mock-server/mock-server.model.ts index cd491518..8cf79e65 100644 --- a/packages/hoppscotch-backend/src/mock-server/mock-server.model.ts +++ b/packages/hoppscotch-backend/src/mock-server/mock-server.model.ts @@ -18,10 +18,10 @@ import { import { WorkspaceType } from 'src/types/WorkspaceTypes'; // Regex pattern for mock server name validation -// Allows letters, numbers, spaces, dots, underscores, and hyphens -const MOCK_SERVER_NAME_PATTERN = /^[a-zA-Z0-9 ._-]+$/; +// Allows letters, numbers, spaces, dots, brackets, underscores, and hyphens +const MOCK_SERVER_NAME_PATTERN = /^[a-zA-Z0-9 .()[\]{}<>_-]+$/; const MOCK_SERVER_NAME_ERROR_MESSAGE = - 'Name can only contain letters, numbers, spaces, dots, underscores, and hyphens'; + 'Name can only contain letters, numbers, spaces, dots, brackets, underscores, and hyphens'; @ObjectType() export class MockServer { diff --git a/packages/hoppscotch-backend/src/mock-server/mock-server.resolver.ts b/packages/hoppscotch-backend/src/mock-server/mock-server.resolver.ts index 93893e59..1ea8c83d 100644 --- a/packages/hoppscotch-backend/src/mock-server/mock-server.resolver.ts +++ b/packages/hoppscotch-backend/src/mock-server/mock-server.resolver.ts @@ -55,11 +55,12 @@ export class MockServerResolver { } @ResolveField(() => MockServerCollection, { + nullable: true, description: 'Returns the collection of the mock server', }) async collection( @Parent() mockServer: MockServer, - ): Promise { + ): Promise { const collection = await this.mockServerService.getMockServerCollection( mockServer.id, ); diff --git a/packages/hoppscotch-backend/src/mock-server/mock-server.service.spec.ts b/packages/hoppscotch-backend/src/mock-server/mock-server.service.spec.ts index 45a85afc..83801fac 100644 --- a/packages/hoppscotch-backend/src/mock-server/mock-server.service.spec.ts +++ b/packages/hoppscotch-backend/src/mock-server/mock-server.service.spec.ts @@ -335,7 +335,7 @@ describe('MockServerService', () => { } }); - test('should return error when collection not found', async () => { + test('should return null when collection not found', async () => { mockPrisma.mockServer.findUnique.mockResolvedValue(dbMockServer); mockPrisma.userCollection.findUnique.mockResolvedValue(null); @@ -343,9 +343,9 @@ describe('MockServerService', () => { dbMockServer.id, ); - expect(E.isLeft(result)).toBe(true); - if (E.isLeft(result)) { - expect(result.left).toBe(MOCK_SERVER_INVALID_COLLECTION); + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect(result.right).toBe(null); } }); }); @@ -863,6 +863,7 @@ describe('MockServerService', () => { } as any; test('should return example by ID header', async () => { + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); mockPrisma.userCollection.findMany.mockResolvedValue([]); // No child collections mockPrisma.userRequest.findMany.mockResolvedValue([userRequest] as any); @@ -882,6 +883,7 @@ describe('MockServerService', () => { }); test('should return example by name header', async () => { + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); mockPrisma.userCollection.findMany.mockResolvedValue([]); // No child collections mockPrisma.userRequest.findMany.mockResolvedValue([userRequest] as any); @@ -915,6 +917,7 @@ describe('MockServerService', () => { }, }; + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); mockPrisma.userCollection.findMany.mockResolvedValue([]); // No child collections mockPrisma.userRequest.findMany.mockResolvedValue([ requestWith404, @@ -936,6 +939,7 @@ describe('MockServerService', () => { }); test('should match exact path', async () => { + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); mockPrisma.userRequest.findMany.mockResolvedValue([userRequest] as any); mockPrisma.userCollection.findMany.mockResolvedValue([]); @@ -964,6 +968,7 @@ describe('MockServerService', () => { }, }; + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); mockPrisma.userRequest.findMany.mockResolvedValue([ variableRequest, ] as any); @@ -979,6 +984,7 @@ describe('MockServerService', () => { }); test('should return error when no examples found', async () => { + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); mockPrisma.userRequest.findMany.mockResolvedValue([]); mockPrisma.userCollection.findMany.mockResolvedValue([]); @@ -1004,6 +1010,7 @@ describe('MockServerService', () => { }, }; + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); mockPrisma.userRequest.findMany.mockResolvedValue([ multipleExamples, ] as any); @@ -1036,6 +1043,7 @@ describe('MockServerService', () => { }, }; + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); mockPrisma.userCollection.findMany.mockResolvedValue([]); // No child collections mockPrisma.userRequest.findMany.mockResolvedValue([simpleRequest] as any); @@ -1070,6 +1078,7 @@ describe('MockServerService', () => { }, }; + mockPrisma.teamCollection.findUnique.mockResolvedValue(teamCollection); mockPrisma.teamCollection.findMany.mockResolvedValue([]); // No child collections mockPrisma.teamRequest.findMany.mockResolvedValue([teamRequest] as any); diff --git a/packages/hoppscotch-backend/src/mock-server/mock-server.service.ts b/packages/hoppscotch-backend/src/mock-server/mock-server.service.ts index 3dab1df0..133a77cb 100644 --- a/packages/hoppscotch-backend/src/mock-server/mock-server.service.ts +++ b/packages/hoppscotch-backend/src/mock-server/mock-server.service.ts @@ -222,7 +222,7 @@ export class MockServerService { const collection = await this.prisma.userCollection.findUnique({ where: { id: mockServer.collectionID }, }); - if (!collection) return E.left(MOCK_SERVER_INVALID_COLLECTION); + if (!collection) return E.right(null); return E.right({ id: collection.id, title: collection.title, @@ -231,7 +231,7 @@ export class MockServerService { const collection = await this.prisma.teamCollection.findUnique({ where: { id: mockServer.collectionID }, }); - if (!collection) return E.left(MOCK_SERVER_INVALID_COLLECTION); + if (!collection) return E.right(null); return E.right({ id: collection.id, title: collection.title, @@ -599,6 +599,12 @@ export class MockServerService { // This is used by both custom header lookup and candidate fetching const collectionIds = await this.getCollectionIds(mockServer); + if (collectionIds.length === 0) { + return E.left( + `The collection associated with this mock has been deleted.`, + ); + } + // OPTIMIZATION: Fetch all requests with examples once (single DB query) // This is shared between custom header lookup and candidate matching const requests = await this.fetchRequestsWithExamples( @@ -848,6 +854,13 @@ export class MockServerService { private async getAllUserCollectionIds( rootCollectionId: string, ): Promise { + // First verify the root collection exists + const rootCollection = await this.prisma.userCollection.findUnique({ + where: { id: rootCollectionId }, + }); + + if (!rootCollection) return []; // Collection doesn't exist + const ids = [rootCollectionId]; const children = await this.prisma.userCollection.findMany({ where: { parentID: rootCollectionId }, @@ -868,6 +881,13 @@ export class MockServerService { private async getAllTeamCollectionIds( rootCollectionId: string, ): Promise { + // First verify the root collection exists + const rootCollection = await this.prisma.teamCollection.findUnique({ + where: { id: rootCollectionId }, + }); + + if (!rootCollection) return []; // Collection doesn't exist + const ids = [rootCollectionId]; const children = await this.prisma.teamCollection.findMany({ where: { parentID: rootCollectionId }, diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index 932d4813..4d800091 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -855,6 +855,7 @@ "authentication": "Authentication" }, "mock_server": { + "confirm_delete_log": "Are you sure you want to delete this log?", "create_mock_server": "Configure Mock Server", "mock_server_configuration": "Mock Server Configuration", "mock_server_name": "Mock Server Name", @@ -885,6 +886,7 @@ "private_description": "Only authenticated users can access this mock server", "select_collection": "Select a collection", "select_collection_error": "Please select a collection", + "invalid_collection_error": "Failed to create a mock server for the collection.", "url_copied": "URL copied to clipboard", "make_public": "Make Public", "view_logs": "View logs", @@ -1079,6 +1081,7 @@ "ai_request_naming_style_custom": "Custom", "ai_request_naming_style_custom_placeholder": "Enter your custom naming style template...", "experimental_scripting_sandbox": "Experimental scripting sandbox", + "enable_experimental_mock_servers": "Enable Mock Servers", "sync": "Synchronise", "sync_collections": "Collections", "sync_description": "These settings are synced to cloud.", @@ -1375,6 +1378,7 @@ "loading": "Loading...", "message_received": "Message: {message} arrived on topic: {topic}", "mqtt_subscription_failed": "Something went wrong while subscribing to topic: {topic}", + "no_content_found": "No content found", "none": "None", "nothing_found": "Nothing found for", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index e356b63f..5e2b21ea 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -214,9 +214,11 @@ declare module 'vue' { IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] IconLucideBrush: typeof import('~icons/lucide/brush')['default'] + IconLucideCheck: typeof import('~icons/lucide/check')['default'] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] IconLucideCircleCheck: typeof import('~icons/lucide/circle-check')['default'] + IconLucideCopy: typeof import('~icons/lucide/copy')['default'] IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default'] IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] @@ -260,6 +262,7 @@ declare module 'vue' { LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default'] MockServerCreateMockServer: typeof import('./components/mockServer/CreateMockServer.vue')['default'] MockServerEditMockServer: typeof import('./components/mockServer/EditMockServer.vue')['default'] + MockServerLogSection: typeof import('./components/mockServer/LogSection.vue')['default'] MockServerMockServerDashboard: typeof import('./components/mockServer/MockServerDashboard.vue')['default'] MockServerMockServerLogs: typeof import('./components/mockServer/MockServerLogs.vue')['default'] MonacoScriptEditor: typeof import('./components/MonacoScriptEditor.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/collections/Collection.vue b/packages/hoppscotch-common/src/components/collections/Collection.vue index 23cac282..9ae1eccb 100644 --- a/packages/hoppscotch-common/src/components/collections/Collection.vue +++ b/packages/hoppscotch-common/src/components/collections/Collection.vue @@ -135,7 +135,10 @@ @keyup.p="propertiesAction?.$el.click()" @keyup.t="runCollectionAction?.$el.click()" @keyup.s="sortAction?.$el.click()" - @keyup.m="mockServerAction?.$el.click()" + @keyup.m=" + ENABLE_EXPERIMENTAL_MOCK_SERVERS && + mockServerAction?.$el.click() + " @keyup.escape="hide()" > { }) // Mock Server Status +const ENABLE_EXPERIMENTAL_MOCK_SERVERS = useSetting( + "ENABLE_EXPERIMENTAL_MOCK_SERVERS" +) const { getMockServerStatus } = useMockServerStatus() const mockServerStatus = computed(() => { + if (!ENABLE_EXPERIMENTAL_MOCK_SERVERS.value) { + return { exists: false, isActive: false } + } + const collectionId = props.collectionsType === "my-collections" ? (props.data as HoppCollection).id diff --git a/packages/hoppscotch-common/src/components/collections/ImportExport.vue b/packages/hoppscotch-common/src/components/collections/ImportExport.vue index b3dba8e8..3eb58aad 100644 --- a/packages/hoppscotch-common/src/components/collections/ImportExport.vue +++ b/packages/hoppscotch-common/src/components/collections/ImportExport.vue @@ -33,7 +33,11 @@ import { defineStep } from "~/composables/step-components" import AllCollectionImport from "~/components/importExport/ImportExportSteps/AllCollectionImport.vue" import { useI18n } from "~/composables/i18n" import { useToast } from "~/composables/toast" -import { appendRESTCollections, restCollections$ } from "~/newstore/collections" +import { + appendRESTCollections, + restCollections$, + setRESTCollections, +} from "~/newstore/collections" import IconInsomnia from "~icons/hopp/insomnia" import IconPostman from "~icons/hopp/postman" @@ -46,6 +50,11 @@ import { useReadonlyStream } from "~/composables/stream" import IconUser from "~icons/lucide/user" import { getTeamCollectionJSON } from "~/helpers/backend/helpers" +import { + importUserCollectionsFromJSON, + fetchAndConvertUserCollections, +} from "~/helpers/backend/mutations/UserCollection" +import { ReqType } from "~/helpers/backend/graphql" import { platform } from "~/platform" @@ -101,7 +110,7 @@ const showImportFailedError = () => { const handleImportToStore = async (collections: HoppCollection[]) => { const importResult = props.collectionsType.type === "my-collections" - ? importToPersonalWorkspace(collections) + ? await importToPersonalWorkspace(collections) : await importToTeamsWorkspace(collections) if (E.isRight(importResult)) { @@ -111,11 +120,58 @@ const handleImportToStore = async (collections: HoppCollection[]) => { } } -const importToPersonalWorkspace = (collections: HoppCollection[]) => { - appendRESTCollections(collections) - return E.right({ - success: true, - }) +const importToPersonalWorkspace = async (collections: HoppCollection[]) => { + // If user is logged in, try to import to backend first + if (currentUser.value) { + try { + const transformedCollection = collections.map((collection) => + translateToPersonalCollectionFormat(collection) + ) + + const res = await importUserCollectionsFromJSON( + JSON.stringify(transformedCollection), + ReqType.Rest + )() + + if (E.isRight(res)) { + // Backend import succeeded, now fetch and persist collections in store + const fetchResult = await fetchAndConvertUserCollections(ReqType.Rest) + + if (E.isRight(fetchResult)) { + // Replace local collections with backend collections + setRESTCollections(fetchResult.right) + } else { + console.warn( + "Failed to fetch collections from backend after import:", + fetchResult.left + ) + // Still append to local store as fallback + appendRESTCollections(collections) + } + + return E.right({ success: true }) + } + // Backend import failed, fall back to local storage + console.warn( + "Backend import failed, falling back to local storage:", + res.left + ) + appendRESTCollections(collections) + return E.right({ success: true }) + } catch (error) { + // Backend import failed, fall back to local storage + console.warn( + "Backend import failed, falling back to local storage:", + error + ) + appendRESTCollections(collections) + return E.right({ success: true }) + } + } else { + // User not logged in, use local storage + appendRESTCollections(collections) + return E.right({ success: true }) + } } function translateToTeamCollectionFormat(x: HoppCollection) { @@ -140,6 +196,28 @@ function translateToTeamCollectionFormat(x: HoppCollection) { return obj } +function translateToPersonalCollectionFormat(x: HoppCollection) { + const folders: HoppCollection[] = (x.folders ?? []).map( + translateToPersonalCollectionFormat + ) + + const data = { + auth: x.auth, + headers: x.headers, + variables: x.variables, + } + + const obj = { + ...x, + folders, + data, + } + + if (x.id) obj.id = x.id + + return obj +} + const importToTeamsWorkspace = async (collections: HoppCollection[]) => { if (!hasTeamWriteAccess.value || !selectedTeamID.value) { return E.left({ diff --git a/packages/hoppscotch-common/src/components/collections/SaveRequest.vue b/packages/hoppscotch-common/src/components/collections/SaveRequest.vue index ff5b28b0..8931046e 100644 --- a/packages/hoppscotch-common/src/components/collections/SaveRequest.vue +++ b/packages/hoppscotch-common/src/components/collections/SaveRequest.vue @@ -154,7 +154,7 @@ import { TeamWorkspace } from "~/services/workspace.service" import IconSparkle from "~icons/lucide/sparkles" import IconThumbsDown from "~icons/lucide/thumbs-down" import IconThumbsUp from "~icons/lucide/thumbs-up" -import { handleTokenValidation } from "~/helpers/handleTokenValidation"; +import { handleTokenValidation } from "~/helpers/handleTokenValidation" const t = useI18n() const toast = useToast() diff --git a/packages/hoppscotch-common/src/components/collections/graphql/ImportExport.vue b/packages/hoppscotch-common/src/components/collections/graphql/ImportExport.vue index 94d13ba7..11a598b7 100644 --- a/packages/hoppscotch-common/src/components/collections/graphql/ImportExport.vue +++ b/packages/hoppscotch-common/src/components/collections/graphql/ImportExport.vue @@ -18,6 +18,11 @@ import { useToast } from "~/composables/toast" import { ImporterOrExporter } from "~/components/importExport/types" import { FileSource } from "~/helpers/import-export/import/import-sources/FileSource" import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource" +import { + importUserCollectionsFromJSON, + fetchAndConvertUserCollections, +} from "~/helpers/backend/mutations/UserCollection" +import { ReqType } from "~/helpers/backend/graphql" import IconFolderPlus from "~icons/lucide/folder-plus" import IconUser from "~icons/lucide/user" @@ -28,6 +33,7 @@ import { platform } from "~/platform" import { appendGraphqlCollections, graphqlCollections$, + setGraphqlCollections, } from "~/newstore/collections" import { hoppGqlCollectionsImporter } from "~/helpers/import-export/import/hoppGql" import { gqlCollectionsExporter } from "~/helpers/import-export/export/gqlCollections" @@ -71,7 +77,7 @@ const GqlCollectionsHoppImporter: ImporterOrExporter = { )() if (E.isRight(validatedCollection)) { - handleImportToStore(validatedCollection.right) + await handleImportToStore(validatedCollection.right) platform.analytics?.logEvent({ type: "HOPP_IMPORT_COLLECTION", @@ -110,7 +116,7 @@ const GqlCollectionsGistImporter: ImporterOrExporter = { return } - handleImportToStore(res.right) + await handleImportToStore(res.right) platform.analytics?.logEvent({ type: "HOPP_IMPORT_COLLECTION", @@ -231,9 +237,83 @@ const showImportFailedError = () => { toast.error(t("import.failed")) } -const handleImportToStore = (gqlCollections: HoppCollection[]) => { - appendGraphqlCollections(gqlCollections) - toast.success(t("state.file_imported")) +const handleImportToStore = async (gqlCollections: HoppCollection[]) => { + // If user is logged in, try to import to backend first + if (currentUser.value) { + try { + const transformedCollection = gqlCollections.map((collection) => + translateToPersonalCollectionFormat(collection) + ) + + const res = await importUserCollectionsFromJSON( + JSON.stringify(transformedCollection), + ReqType.Gql + )() + + if (E.isRight(res)) { + // Backend import succeeded, now fetch and persist collections in store + const fetchResult = await fetchAndConvertUserCollections(ReqType.Gql) + + if (E.isRight(fetchResult)) { + // Replace local collections with backend collections + setGraphqlCollections(fetchResult.right) + } else { + console.warn( + "Failed to fetch collections from backend after import:", + fetchResult.left + ) + // Still append to local store as fallback + appendGraphqlCollections(gqlCollections) + } + + toast.success(t("state.file_imported")) + return + } + // Backend import failed, fall back to local storage + console.warn( + "Backend import failed, falling back to local storage:", + res.left + ) + appendGraphqlCollections(gqlCollections) + toast.success(t("state.file_imported")) + return + } catch (error) { + // Backend import failed, fall back to local storage + console.warn( + "Backend import failed, falling back to local storage:", + error + ) + appendGraphqlCollections(gqlCollections) + toast.success(t("state.file_imported")) + return + } + } else { + // User not logged in, use local storage + appendGraphqlCollections(gqlCollections) + toast.success(t("state.file_imported")) + } +} + +function translateToPersonalCollectionFormat(x: HoppCollection) { + const folders: HoppCollection[] = (x.folders ?? []).map( + translateToPersonalCollectionFormat + ) + + const data = { + auth: x.auth, + headers: x.headers, + variables: x.variables, + } + + const obj = { + ...x, + folders, + data, + } + + if (x.id) obj.id = x.id + + return obj } const emit = defineEmits<{ diff --git a/packages/hoppscotch-common/src/components/collections/index.vue b/packages/hoppscotch-common/src/components/collections/index.vue index f71cab6b..64b94d52 100644 --- a/packages/hoppscotch-common/src/components/collections/index.vue +++ b/packages/hoppscotch-common/src/components/collections/index.vue @@ -1083,12 +1083,7 @@ const createMockServer = (payload: { }) => { // Import the mock server store dynamically to avoid circular dependencies import("~/newstore/mockServers").then(({ showCreateMockServerModal$ }) => { - // For personal collections, use the collection's _ref_id or id - // For child collections, we need to get the root collection ID - let collectionID = - payload.collection.id || - payload.collection._ref_id || - payload.collectionIndex + let collectionID = payload.collection.id ?? undefined // If this is a child collection (folder), we need to get the root collection ID if (payload.collectionIndex.includes("/")) { @@ -1096,7 +1091,7 @@ const createMockServer = (payload: { const rootIndex = payload.collectionIndex.split("/")[0] const rootCollection = myCollections.value[parseInt(rootIndex)] if (rootCollection) { - collectionID = rootCollection.id || rootCollection._ref_id || rootIndex + collectionID = rootCollection.id ?? undefined } } diff --git a/packages/hoppscotch-common/src/components/http/Request.vue b/packages/hoppscotch-common/src/components/http/Request.vue index ffc49810..585bfdec 100644 --- a/packages/hoppscotch-common/src/components/http/Request.vue +++ b/packages/hoppscotch-common/src/components/http/Request.vue @@ -270,7 +270,7 @@ import { RESTTabService } from "~/services/tab/rest" import { getMethodLabelColor } from "~/helpers/rest/labelColoring" import { WorkspaceService } from "~/services/workspace.service" import { KernelInterceptorService } from "~/services/kernel-interceptor.service" -import { handleTokenValidation } from "~/helpers/handleTokenValidation"; +import { handleTokenValidation } from "~/helpers/handleTokenValidation" const t = useI18n() const interceptorService = useService(KernelInterceptorService) diff --git a/packages/hoppscotch-common/src/components/http/Sidebar.vue b/packages/hoppscotch-common/src/components/http/Sidebar.vue index aff2b99b..c77d329e 100644 --- a/packages/hoppscotch-common/src/components/http/Sidebar.vue +++ b/packages/hoppscotch-common/src/components/http/Sidebar.vue @@ -52,6 +52,7 @@ /> ("collections") + +// Ensure mock servers are kept in sync with workspace changes globally +useMockServerWorkspaceSync() diff --git a/packages/hoppscotch-common/src/components/mockServer/CreateMockServer.vue b/packages/hoppscotch-common/src/components/mockServer/CreateMockServer.vue index d330a351..7c60016f 100644 --- a/packages/hoppscotch-common/src/components/mockServer/CreateMockServer.vue +++ b/packages/hoppscotch-common/src/components/mockServer/CreateMockServer.vue @@ -102,15 +102,25 @@
- {{ mockServerBaseUrl }} + {{ + existingMockServer?.serverUrlPathBased || + existingMockServer?.serverUrlDomainBased || + "" + }}
@@ -121,11 +131,11 @@
-
+
-
-

- - {{ t("mock_server.description") }} +

+

+ + + {{ t("mock_server.description") }} +

@@ -237,12 +251,6 @@ - diff --git a/packages/hoppscotch-common/src/components/mockServer/MockServerDashboard.vue b/packages/hoppscotch-common/src/components/mockServer/MockServerDashboard.vue index 0de14ae8..e0b7c941 100644 --- a/packages/hoppscotch-common/src/components/mockServer/MockServerDashboard.vue +++ b/packages/hoppscotch-common/src/components/mockServer/MockServerDashboard.vue @@ -58,13 +58,16 @@ @click="openCreateModal" />
-
+
- +
@@ -201,53 +204,48 @@ diff --git a/packages/hoppscotch-common/src/components/mockServer/MockServerLogs.vue b/packages/hoppscotch-common/src/components/mockServer/MockServerLogs.vue index a226c0a0..e3862dea 100644 --- a/packages/hoppscotch-common/src/components/mockServer/MockServerLogs.vue +++ b/packages/hoppscotch-common/src/components/mockServer/MockServerLogs.vue @@ -3,6 +3,7 @@ v-if="show" dialog :title="t('mock_server.logs_title')" + styles="sm:max-w-4xl" @close="close" > + + diff --git a/packages/hoppscotch-common/src/composables/mockServerWorkspace.ts b/packages/hoppscotch-common/src/composables/mockServerWorkspace.ts new file mode 100644 index 00000000..1efa7f19 --- /dev/null +++ b/packages/hoppscotch-common/src/composables/mockServerWorkspace.ts @@ -0,0 +1,51 @@ +import { onMounted, watch } from "vue" +import { useService } from "dioc/vue" +import { WorkspaceService } from "~/services/workspace.service" +import { setMockServers, loadMockServers } from "~/newstore/mockServers" +import { platform } from "~/platform" +import { useSetting } from "./settings" + +/** + * Composable to handle mock server state when workspace changes + * This ensures mock servers are cleared immediately when switching workspaces + * to prevent showing stale data from the previous workspace + */ +export function useMockServerWorkspaceSync() { + const workspaceService = useService(WorkspaceService) + const ENABLE_EXPERIMENTAL_MOCK_SERVERS = useSetting( + "ENABLE_EXPERIMENTAL_MOCK_SERVERS" + ) + const isAuthenticated = !!platform.auth.getCurrentUser() + + // Initial load of mock servers for the current workspace + onMounted(() => { + if (!isAuthenticated || !ENABLE_EXPERIMENTAL_MOCK_SERVERS.value) return + loadMockServers().catch(() => setMockServers([])) + }) + + // Watch for workspace changes and clear mock servers immediately + watch( + () => workspaceService.currentWorkspace.value, + (newWorkspace, oldWorkspace) => { + if (!isAuthenticated || !ENABLE_EXPERIMENTAL_MOCK_SERVERS.value) return + + // Clear mock servers when workspace changes to prevent stale data + if ( + newWorkspace?.type !== oldWorkspace?.type || + (newWorkspace?.type === "team" && + oldWorkspace?.type === "team" && + newWorkspace.teamID !== oldWorkspace.teamID) + ) { + // Clear mock servers immediately to prevent showing stale data + setMockServers([]) + + // If user is authenticated, reload mock servers for the new workspace + if (platform.auth.getCurrentUser()) { + // fire-and-forget; loadMockServers handles errors internally + loadMockServers().catch(() => setMockServers([])) + } + } + }, + { deep: true, immediate: false } + ) +} diff --git a/packages/hoppscotch-common/src/helpers/backend/gql/mutations/ImportUserCollectionsFromJSON.graphql b/packages/hoppscotch-common/src/helpers/backend/gql/mutations/ImportUserCollectionsFromJSON.graphql new file mode 100644 index 00000000..eced5dc4 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/backend/gql/mutations/ImportUserCollectionsFromJSON.graphql @@ -0,0 +1,11 @@ +mutation ImportUserCollectionsFromJSON( + $jsonString: String! + $reqType: ReqType! + $parentCollectionID: ID +) { + importUserCollectionsFromJSON( + jsonString: $jsonString + reqType: $reqType + parentCollectionID: $parentCollectionID + ) +} \ No newline at end of file diff --git a/packages/hoppscotch-common/src/helpers/backend/gql/queries/GetGQLRootUserCollections.graphql b/packages/hoppscotch-common/src/helpers/backend/gql/queries/GetGQLRootUserCollections.graphql new file mode 100644 index 00000000..b840ac9b --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/backend/gql/queries/GetGQLRootUserCollections.graphql @@ -0,0 +1,34 @@ +query GetGQLRootUserCollections($cursor: ID, $take: Int) { + rootGQLUserCollections(cursor: $cursor, take: $take) { + id + title + data + type + parent { + id + } + requests { + id + title + request + type + collectionID + } + childrenGQL { + id + title + data + type + parent { + id + } + requests { + id + title + request + type + collectionID + } + } + } +} \ No newline at end of file diff --git a/packages/hoppscotch-common/src/helpers/backend/gql/queries/GetUserRootCollections.graphql b/packages/hoppscotch-common/src/helpers/backend/gql/queries/GetUserRootCollections.graphql new file mode 100644 index 00000000..40c3f22e --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/backend/gql/queries/GetUserRootCollections.graphql @@ -0,0 +1,34 @@ +query GetUserRootCollections($cursor: ID, $take: Int) { + rootRESTUserCollections(cursor: $cursor, take: $take) { + id + title + data + type + parent { + id + } + requests { + id + title + request + type + collectionID + } + childrenREST { + id + title + data + type + parent { + id + } + requests { + id + title + request + type + collectionID + } + } + } +} \ No newline at end of file diff --git a/packages/hoppscotch-common/src/helpers/backend/mutations/MockServer.ts b/packages/hoppscotch-common/src/helpers/backend/mutations/MockServer.ts index 0d14f3c7..71d3ca70 100644 --- a/packages/hoppscotch-common/src/helpers/backend/mutations/MockServer.ts +++ b/packages/hoppscotch-common/src/helpers/backend/mutations/MockServer.ts @@ -1,5 +1,7 @@ import * as TE from "fp-ts/TaskEither" import { client } from "../GQLClient" +import { GQLError } from "../GQLClient" +import { getI18n } from "~/modules/i18n" import { CreateMockServerDocument, UpdateMockServerDocument, @@ -36,6 +38,7 @@ export type MockServer = { } type CreateMockServerError = + | "mock_server/invalid_collection" | "mock_server/invalid_collection_id" | "mock_server/name_too_short" | "mock_server/limit_exceeded" @@ -73,7 +76,26 @@ export const createMockServer = ( .toPromise() if (result.error) { - throw new Error(result.error.message || "Failed to create mock server") + // Try to extract a useful error message from the GraphQL error + const err: any = result.error + let message = err.message + + // urql exposes GraphQL errors in graphQLErrors array + const gqlErr = (err.graphQLErrors && err.graphQLErrors[0]) || null + if (gqlErr) { + // Prefer originalError.message from backend if present (it may be an array of messages) + const orig = + gqlErr.extensions && + gqlErr.extensions.originalError && + gqlErr.extensions.originalError.message + if (orig) { + message = Array.isArray(orig) ? orig.join(", ") : String(orig) + } else if (gqlErr.message) { + message = gqlErr.message + } + } + + throw new Error(message) } if (!result.data) { @@ -183,3 +205,36 @@ export const getTeamMockServers = ( }, (error) => (error as Error).message as CreateMockServerError ) + +// Centralized mapper for backend GraphQL error tokens to user-facing messages. +export const getErrorMessage = (err: GQLError | string | Error) => { + const t = getI18n() + + // Normalize to GQLError-like shape + let gErr: GQLError | null = null + + if (typeof err === "string") { + gErr = { type: "gql_error", error: err } + } else if (err instanceof Error) { + gErr = { type: "network_error", error: err } + } else if ((err as any)?.type) { + gErr = err as GQLError + } + + if (!gErr) return t("error.something_went_wrong") + + if (gErr.type === "network_error") { + console.error(gErr.error) + return t("error.network_error") + } + + const code = String(gErr.error) + + switch (code) { + case "mock_server/invalid_collection": + case "mock_server/invalid_collection_id": + return t("mock_server.invalid_collection_error") + default: + return t("error.something_went_wrong") + } +} diff --git a/packages/hoppscotch-common/src/helpers/backend/mutations/UserCollection.ts b/packages/hoppscotch-common/src/helpers/backend/mutations/UserCollection.ts new file mode 100644 index 00000000..3329ccc2 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/backend/mutations/UserCollection.ts @@ -0,0 +1,203 @@ +import { runMutation } from "../GQLClient" +import { runGQLQuery } from "../GQLClient" +import { + GetGqlRootUserCollectionsDocument, + GetGqlRootUserCollectionsQuery, + GetGqlRootUserCollectionsQueryVariables, + GetUserRootCollectionsDocument, + GetUserRootCollectionsQuery, + GetUserRootCollectionsQueryVariables, + ImportUserCollectionsFromJsonDocument, + ImportUserCollectionsFromJsonMutation, + ImportUserCollectionsFromJsonMutationVariables, + ReqType, + UserCollection, + UserRequest, +} from "../graphql" +import { + HoppCollection, + makeCollection, + HoppRESTRequest, + HoppGQLRequest, + getDefaultRESTRequest, + getDefaultGQLRequest, +} from "@hoppscotch/data" +import * as E from "fp-ts/Either" + +export const importUserCollectionsFromJSON = ( + collectionJSON: string, + reqType: ReqType, + parentCollectionID?: string +) => + runMutation< + ImportUserCollectionsFromJsonMutation, + ImportUserCollectionsFromJsonMutationVariables, + "" + >(ImportUserCollectionsFromJsonDocument, { + jsonString: collectionJSON, + reqType, + parentCollectionID, + }) + +// Use generated GraphQL documents instead of inline gql tags +export const getUserRootCollections = () => + runGQLQuery< + GetUserRootCollectionsQuery, + GetUserRootCollectionsQueryVariables, + "" + >({ + query: GetUserRootCollectionsDocument, + variables: {}, + }) + +export const getGQLRootUserCollections = () => + runGQLQuery< + GetGqlRootUserCollectionsQuery, + GetGqlRootUserCollectionsQueryVariables, + "" + >({ + query: GetGqlRootUserCollectionsDocument, + variables: {}, + }) + +/** + * Converts a UserRequest from backend format to HoppRequest format + */ +function convertUserRequestToHoppRequest( + userRequest: UserRequest +): HoppRESTRequest | HoppGQLRequest { + try { + const parsedRequest = JSON.parse(userRequest.request) + + // Add the backend ID and title to the request + const request = { + ...parsedRequest, + id: userRequest.id, + name: userRequest.title, + } + + return request + } catch (error) { + console.warn("Failed to parse user request data:", error) + + // Return a default request if parsing fails + if (userRequest.type === ReqType.Rest) { + const defaultRequest = getDefaultRESTRequest() + defaultRequest.id = userRequest.id + defaultRequest.name = userRequest.title + return defaultRequest + } + const defaultRequest = getDefaultGQLRequest() + defaultRequest.id = userRequest.id + defaultRequest.name = userRequest.title + return defaultRequest + } +} + +/** + * Parse collection data similar to the existing parseCollectionData function in helpers.ts + */ +function parseUserCollectionData(data: string | null | undefined) { + const defaultDataProps = { + auth: { authType: "inherit", authActive: true }, + headers: [], + variables: [], + } + + if (!data) { + return defaultDataProps + } + + try { + const parsedData = JSON.parse(data) + return { + auth: parsedData?.auth || defaultDataProps.auth, + headers: parsedData?.headers || defaultDataProps.headers, + variables: parsedData?.variables || defaultDataProps.variables, + } + } catch (error) { + console.warn("Failed to parse user collection data:", error) + return defaultDataProps + } +} + +/** + * Converts a UserCollection from backend format to HoppCollection format + * Following the same pattern as teamCollectionJSONToHoppRESTColl in helpers.ts + */ +export function convertUserCollectionToHoppCollection( + userCollection: UserCollection, + reqType: ReqType +): HoppCollection { + const { auth, headers, variables } = parseUserCollectionData( + userCollection.data + ) + + // Get the appropriate children based on request type + const children = + reqType === ReqType.Rest + ? userCollection.childrenREST + : userCollection.childrenGQL + + // Convert requests - filter by type and convert + const requests = userCollection.requests + ? userCollection.requests + .filter((req) => req.type === reqType) + .map(convertUserRequestToHoppRequest) + : [] + + const collection = makeCollection({ + name: userCollection.title, + folders: children + ? children.map((child) => + convertUserCollectionToHoppCollection(child, reqType) + ) + : [], + requests: requests, + auth, + headers, + variables, + }) + + // Add the backend ID to the collection + collection.id = userCollection.id + + return collection +} + +/** + * Fetches user collections from backend and converts them to HoppCollection format + */ +export const fetchAndConvertUserCollections = async (reqType: ReqType) => { + const fetchFunction = + reqType === ReqType.Rest + ? getUserRootCollections + : getGQLRootUserCollections + + const result = await fetchFunction() + + if (E.isLeft(result)) { + return E.left(result.left) + } + + if (reqType === ReqType.Rest) { + const right = result.right as GetUserRootCollectionsQuery + const collections = right.rootRESTUserCollections + const convertedCollections = collections.map((collection) => + convertUserCollectionToHoppCollection( + collection as unknown as UserCollection, + reqType + ) + ) + return E.right(convertedCollections) + } + const right = result.right as GetGqlRootUserCollectionsQuery + const collections = right.rootGQLUserCollections + const convertedCollections = collections.map((collection) => + convertUserCollectionToHoppCollection( + collection as unknown as UserCollection, + reqType + ) + ) + return E.right(convertedCollections) +} diff --git a/packages/hoppscotch-common/src/newstore/mockServers.ts b/packages/hoppscotch-common/src/newstore/mockServers.ts index 6a1f415e..df5ea8b4 100644 --- a/packages/hoppscotch-common/src/newstore/mockServers.ts +++ b/packages/hoppscotch-common/src/newstore/mockServers.ts @@ -1,14 +1,14 @@ -import { pluck } from "rxjs/operators" +import { pipe } from "fp-ts/function" +import * as TE from "fp-ts/TaskEither" import { BehaviorSubject } from "rxjs" -import DispatchingStore, { defineDispatchers } from "./DispatchingStore" +import { pluck } from "rxjs/operators" import { getMyMockServers, getTeamMockServers, } from "~/helpers/backend/queries/MockServer" -import { pipe } from "fp-ts/function" -import * as TE from "fp-ts/TaskEither" import { getService } from "~/modules/dioc" import { WorkspaceService } from "~/services/workspace.service" +import DispatchingStore, { defineDispatchers } from "./DispatchingStore" export type WorkspaceType = "USER" | "TEAM" @@ -166,6 +166,8 @@ export function loadMockServers(skip?: number, take?: number) { TE.match( (error) => { console.error("Failed to load mock servers:", error) + // Clear mock servers on error to prevent stale data + setMockServers([]) }, (mockServers) => { setMockServers(mockServers) @@ -179,6 +181,8 @@ export function loadMockServers(skip?: number, take?: number) { TE.match( (error) => { console.error("Failed to load mock servers:", error) + // Clear mock servers on error to prevent stale data + setMockServers([]) }, (mockServers) => { setMockServers(mockServers) @@ -199,6 +203,8 @@ export function loadTeamMockServers( TE.match( (error) => { console.error("Failed to load team mock servers:", error) + // Clear mock servers on error to prevent stale data + setMockServers([]) }, (mockServers) => { setMockServers(mockServers) @@ -214,6 +220,9 @@ export function loadMockServersForWorkspace( skip?: number, take?: number ) { + // Clear existing mock servers first to prevent stale data + setMockServers([]) + if (workspaceType === "team" && teamID) { return loadTeamMockServers(teamID, skip, take) } diff --git a/packages/hoppscotch-common/src/newstore/settings.ts b/packages/hoppscotch-common/src/newstore/settings.ts index 6b9cfef7..e4de4cc1 100644 --- a/packages/hoppscotch-common/src/newstore/settings.ts +++ b/packages/hoppscotch-common/src/newstore/settings.ts @@ -84,6 +84,7 @@ export type SettingsDef = { CUSTOM_NAMING_STYLE: string EXPERIMENTAL_SCRIPTING_SANDBOX: boolean + ENABLE_EXPERIMENTAL_MOCK_SERVERS: boolean } let defaultProxyURL = DEFAULT_HOPP_PROXY_URL @@ -146,6 +147,7 @@ export const getDefaultSettings = (): SettingsDef => { CUSTOM_NAMING_STYLE: "", EXPERIMENTAL_SCRIPTING_SANDBOX: true, + ENABLE_EXPERIMENTAL_MOCK_SERVERS: true, } } diff --git a/packages/hoppscotch-common/src/pages/settings.vue b/packages/hoppscotch-common/src/pages/settings.vue index ff2a0dee..5d63c47a 100644 --- a/packages/hoppscotch-common/src/pages/settings.vue +++ b/packages/hoppscotch-common/src/pages/settings.vue @@ -156,7 +156,7 @@
-
+
+
+ + {{ t("settings.enable_experimental_mock_servers") }} + +
@@ -354,6 +362,9 @@ const CUSTOM_NAMING_STYLE = useSetting("CUSTOM_NAMING_STYLE") const EXPERIMENTAL_SCRIPTING_SANDBOX = useSetting( "EXPERIMENTAL_SCRIPTING_SANDBOX" ) +const ENABLE_EXPERIMENTAL_MOCK_SERVERS = useSetting( + "ENABLE_EXPERIMENTAL_MOCK_SERVERS" +) const supportedNamingStyles = [ { 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 10e94aaf..036138fa 100644 --- a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts +++ b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts @@ -85,6 +85,7 @@ const SettingsDefSchema = z.object({ CUSTOM_NAMING_STYLE: z.string().optional().catch(""), EXPERIMENTAL_SCRIPTING_SANDBOX: z.optional(z.boolean()), + ENABLE_EXPERIMENTAL_MOCK_SERVERS: z.optional(z.boolean()), }) const HoppRESTRequestSchema = entityReference(HoppRESTRequest) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.platform.ts b/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.platform.ts index f4c535c2..fe671c9c 100644 --- a/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.platform.ts +++ b/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.platform.ts @@ -51,7 +51,6 @@ import { updateRESTCollectionOrder, updateRESTRequestOrder, } from "@hoppscotch/common/newstore/collections" -import { loadMockServers } from "@hoppscotch/common/newstore/mockServers" import { GQLHeader, HoppCollection, @@ -84,7 +83,6 @@ function initCollectionsSync() { if (user) { loadUserCollections("REST") loadUserCollections("GQL") - loadMockServers() } }) diff --git a/packages/hoppscotch-selfhost-web/src/platform/collections/desktop/index.ts b/packages/hoppscotch-selfhost-web/src/platform/collections/desktop/index.ts index 5d64aeb2..59b77e20 100644 --- a/packages/hoppscotch-selfhost-web/src/platform/collections/desktop/index.ts +++ b/packages/hoppscotch-selfhost-web/src/platform/collections/desktop/index.ts @@ -1,6 +1,6 @@ import { CollectionsPlatformDef } from "@hoppscotch/common/platform/collections" -import { authEvents$, def as platformAuth } from "@platform/auth/desktop" import { runDispatchWithOutSyncing } from "@lib/sync" +import { authEvents$, def as platformAuth } from "@platform/auth/desktop" import { exportUserCollectionsToJSON, @@ -19,6 +19,11 @@ import { } from "./api" import { collectionsSyncer, getStoreByCollectionType } from "./sync" +import { + ReqType, + UserCollectionDuplicatedData, + UserRequest, +} from "@api/generated/graphql" import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient" import { addGraphqlCollection, @@ -51,7 +56,6 @@ import { updateRESTCollectionOrder, updateRESTRequestOrder, } from "@hoppscotch/common/newstore/collections" -import { loadMockServers } from "@hoppscotch/common/newstore/mockServers" import { generateUniqueRefId, GQLHeader, @@ -62,11 +66,6 @@ import { HoppRESTRequest, } from "@hoppscotch/data" import * as E from "fp-ts/Either" -import { - ReqType, - UserCollectionDuplicatedData, - UserRequest, -} from "@api/generated/graphql" import { gqlCollectionsSyncer } from "./gqlCollections.sync" function initCollectionsSync() { @@ -85,7 +84,6 @@ function initCollectionsSync() { if (user) { loadUserCollections("REST") loadUserCollections("GQL") - loadMockServers() } }) diff --git a/packages/hoppscotch-selfhost-web/src/platform/collections/web/index.ts b/packages/hoppscotch-selfhost-web/src/platform/collections/web/index.ts index 0e315d63..576cfcc2 100644 --- a/packages/hoppscotch-selfhost-web/src/platform/collections/web/index.ts +++ b/packages/hoppscotch-selfhost-web/src/platform/collections/web/index.ts @@ -1,6 +1,6 @@ import { CollectionsPlatformDef } from "@hoppscotch/common/platform/collections" -import { authEvents$, def as platformAuth } from "@platform/auth/web" import { runDispatchWithOutSyncing } from "@lib/sync" +import { authEvents$, def as platformAuth } from "@platform/auth/web" import { exportUserCollectionsToJSON, @@ -19,6 +19,11 @@ import { } from "./api" import { collectionsSyncer, getStoreByCollectionType } from "./sync" +import { + ReqType, + UserCollectionDuplicatedData, + UserRequest, +} from "@api/generated/graphql" import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient" import { addGraphqlCollection, @@ -51,7 +56,6 @@ import { updateRESTCollectionOrder, updateRESTRequestOrder, } from "@hoppscotch/common/newstore/collections" -import { loadMockServers } from "@hoppscotch/common/newstore/mockServers" import { generateUniqueRefId, GQLHeader, @@ -62,11 +66,6 @@ import { HoppRESTRequest, } from "@hoppscotch/data" import * as E from "fp-ts/Either" -import { - ReqType, - UserCollectionDuplicatedData, - UserRequest, -} from "@api/generated/graphql" import { gqlCollectionsSyncer } from "./gqlCollections.sync" function initCollectionsSync() { @@ -85,7 +84,6 @@ function initCollectionsSync() { if (user) { loadUserCollections("REST") loadUserCollections("GQL") - loadMockServers() } }) diff --git a/packages/hoppscotch-sh-admin/locales/en.json b/packages/hoppscotch-sh-admin/locales/en.json index 5ec5955b..7e185125 100644 --- a/packages/hoppscotch-sh-admin/locales/en.json +++ b/packages/hoppscotch-sh-admin/locales/en.json @@ -136,9 +136,8 @@ "title": "Mock Server", "description": "Configure mock server settings used to host example responses.", "wildcard_domain": "Wildcard Domain", - "wildcard_domain_placeholder": "e.g. *.example.com", - "secure_cookies": "Allow secure cookies", - "secure_cookies_desc": "Use secure cookies for responses from the mock server" + "wildcard_domain_description": "This field requires a full wildcard domain format. The input must start with an asterisk (*) followed by a dot (.) and then the domain name.", + "wildcard_domain_example": "Example: *.mock.domain.com" }, "update_failure": "Failed to update server configurations" }, diff --git a/packages/hoppscotch-sh-admin/src/components/settings/MockServerConfig.vue b/packages/hoppscotch-sh-admin/src/components/settings/MockServerConfig.vue index 172a9f9f..76219d87 100644 --- a/packages/hoppscotch-sh-admin/src/components/settings/MockServerConfig.vue +++ b/packages/hoppscotch-sh-admin/src/components/settings/MockServerConfig.vue @@ -17,29 +17,19 @@ - -
- -
-
-
- {{ t('configs.mock_server.secure_cookies') }} -
-

- {{ t('configs.mock_server.secure_cookies_desc') }} -

-
- +

+ {{ t('configs.mock_server.wildcard_domain_description') }} +

+

+ {{ t('configs.mock_server.wildcard_domain_example') }} +

@@ -69,7 +59,6 @@ const mockFields = computed({ return ( workingConfigs.value.mockServerConfigs?.fields ?? { mock_server_wildcard_domain: '', - allow_secure_cookies: false, } ); }, diff --git a/packages/hoppscotch-sh-admin/src/composables/useConfigHandler.ts b/packages/hoppscotch-sh-admin/src/composables/useConfigHandler.ts index df3b0f53..d3f6b582 100644 --- a/packages/hoppscotch-sh-admin/src/composables/useConfigHandler.ts +++ b/packages/hoppscotch-sh-admin/src/composables/useConfigHandler.ts @@ -191,8 +191,6 @@ export function useConfigHandler(updatedConfigs?: ServerConfigs) { mock_server_wildcard_domain: getFieldValue( InfraConfigEnum.MockServerWildcardDomain ), - allow_secure_cookies: - getFieldValue(InfraConfigEnum.AllowSecureCookies) === 'true', }, }, }; diff --git a/packages/hoppscotch-sh-admin/src/helpers/configs.ts b/packages/hoppscotch-sh-admin/src/helpers/configs.ts index 89e95ab6..03271bfe 100644 --- a/packages/hoppscotch-sh-admin/src/helpers/configs.ts +++ b/packages/hoppscotch-sh-admin/src/helpers/configs.ts @@ -92,7 +92,6 @@ export type ServerConfigs = { name: string; fields: { mock_server_wildcard_domain: string; - allow_secure_cookies: boolean; }; }; }; @@ -289,10 +288,6 @@ export const MOCK_SERVER_CONFIGS: Config[] = [ name: InfraConfigEnum.MockServerWildcardDomain, key: 'mock_server_wildcard_domain', }, - { - name: InfraConfigEnum.AllowSecureCookies, - key: 'allow_secure_cookies', - }, ]; export const ALL_CONFIGS = [