From e607f9db24cfb0d83caa49ada5519422e8c07b71 Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Thu, 30 Oct 2025 15:47:16 +0600 Subject: [PATCH] feat(common): mock server ui improvements (#5532) - Update active state styles for better visibility in the mock server. - BE updates catered to improving content type handling in the mock server. - Introduced a `disableMockServerInPersonalWorkspace` platform-level feature flag. - Remove inactive keyboard shorthand nudges from the Mock server dashboard context menu. --- Co-authored-by: mirarifhasan Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> --- .../src/mock-server/mock-server.controller.ts | 31 +++++++++---- .../src/components/collections/Collection.vue | 13 +++--- .../src/components/http/Sidebar.vue | 8 ++-- .../mockServer/CreateMockServer.vue | 6 +-- .../components/mockServer/EditMockServer.vue | 6 +-- .../mockServer/MockServerDashboard.vue | 2 - .../src/composables/mockServerVisibility.ts | 45 +++++++++++++++++++ .../src/composables/mockServerWorkspace.ts | 10 ++--- .../hoppscotch-common/src/platform/index.ts | 6 +++ 9 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 packages/hoppscotch-common/src/composables/mockServerVisibility.ts diff --git a/packages/hoppscotch-backend/src/mock-server/mock-server.controller.ts b/packages/hoppscotch-backend/src/mock-server/mock-server.controller.ts index 66a4e810..b2432796 100644 --- a/packages/hoppscotch-backend/src/mock-server/mock-server.controller.ts +++ b/packages/hoppscotch-backend/src/mock-server/mock-server.controller.ts @@ -87,6 +87,7 @@ export class MockServerController { try { const headers = JSON.parse(mockResponse.headers); Object.keys(headers).forEach((key) => { + console.log('Setting header:', key, headers[key]); res.setHeader(key, headers[key]); }); } catch (error) { @@ -101,17 +102,31 @@ export class MockServerController { ); } - // Send response - const defaultContentType = - typeof mockResponse.body === 'object' - ? 'application/json' - : 'text/plain'; - const contentType = - mockResponse.headers?.['content-type'] || defaultContentType; + // Only set Content-Type if not already set + if (!res.getHeader('Content-Type')) { + let defaultContentType = 'text/plain'; - res.setHeader('Content-Type', contentType); + // Check if body is a string and try to parse it to determine content type + if (typeof mockResponse.body === 'string') { + try { + JSON.parse(mockResponse.body); + // If parsing succeeds, it's JSON + defaultContentType = 'application/json'; + } catch { + // If parsing fails, it's plain text + defaultContentType = 'text/plain'; + } + } else if (typeof mockResponse.body === 'object') { + // If it's already an object, it's JSON + defaultContentType = 'application/json'; + } + + res.setHeader('Content-Type', defaultContentType); + } + // Security headers res.setHeader('X-Content-Type-Options', 'nosniff'); + // Send response return res.status(mockResponse.statusCode).send(mockResponse.body); } catch (error) { console.error('Error handling mock request:', error); diff --git a/packages/hoppscotch-common/src/components/collections/Collection.vue b/packages/hoppscotch-common/src/components/collections/Collection.vue index 9ae1eccb..b7245673 100644 --- a/packages/hoppscotch-common/src/components/collections/Collection.vue +++ b/packages/hoppscotch-common/src/components/collections/Collection.vue @@ -136,8 +136,7 @@ @keyup.t="runCollectionAction?.$el.click()" @keyup.s="sortAction?.$el.click()" @keyup.m=" - ENABLE_EXPERIMENTAL_MOCK_SERVERS && - mockServerAction?.$el.click() + isMockServerVisible && mockServerAction?.$el.click() " @keyup.escape="hide()" > @@ -183,7 +182,7 @@ v-if=" !hasNoTeamAccess && isRootCollection && - ENABLE_EXPERIMENTAL_MOCK_SERVERS + isMockServerVisible " ref="mockServerAction" :icon="IconServer" @@ -328,7 +327,7 @@ import IconArrowUpDown from "~icons/lucide/arrow-up-down" import { CurrentSortValuesService } from "~/services/current-sort.service" import { useService } from "dioc/vue" import { useMockServerStatus } from "~/composables/mockServer" -import { useSetting } from "@composables/settings" +import { useMockServerVisibility } from "~/composables/mockServerVisibility" import { platform } from "~/platform" import { invokeAction } from "~/helpers/actions" @@ -464,13 +463,11 @@ const isCollectionLoading = computed(() => { }) // Mock Server Status -const ENABLE_EXPERIMENTAL_MOCK_SERVERS = useSetting( - "ENABLE_EXPERIMENTAL_MOCK_SERVERS" -) +const { isMockServerVisible } = useMockServerVisibility() const { getMockServerStatus } = useMockServerStatus() const mockServerStatus = computed(() => { - if (!ENABLE_EXPERIMENTAL_MOCK_SERVERS.value) { + if (!isMockServerVisible.value) { return { exists: false, isActive: false } } diff --git a/packages/hoppscotch-common/src/components/http/Sidebar.vue b/packages/hoppscotch-common/src/components/http/Sidebar.vue index c77d329e..36ad55dd 100644 --- a/packages/hoppscotch-common/src/components/http/Sidebar.vue +++ b/packages/hoppscotch-common/src/components/http/Sidebar.vue @@ -52,7 +52,7 @@ /> {{ diff --git a/packages/hoppscotch-common/src/components/mockServer/EditMockServer.vue b/packages/hoppscotch-common/src/components/mockServer/EditMockServer.vue index e4ca643e..71b922d4 100644 --- a/packages/hoppscotch-common/src/components/mockServer/EditMockServer.vue +++ b/packages/hoppscotch-common/src/components/mockServer/EditMockServer.vue @@ -68,13 +68,13 @@ class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium" :class=" isActive - ? 'bg-green-600/20 text-green-200 border border-green-900/50' - : 'bg-gray-600/20 text-gray-200 border border-gray-900/50' + ? 'bg-green-600/20 text-green-500 border border-green-600/30' + : 'text-secondary border border-secondaryLight' " > {{ isActive diff --git a/packages/hoppscotch-common/src/components/mockServer/MockServerDashboard.vue b/packages/hoppscotch-common/src/components/mockServer/MockServerDashboard.vue index 4f379eb1..3a0c05e1 100644 --- a/packages/hoppscotch-common/src/components/mockServer/MockServerDashboard.vue +++ b/packages/hoppscotch-common/src/components/mockServer/MockServerDashboard.vue @@ -150,7 +150,6 @@ ? t('mock_server.stop_server') : t('mock_server.start_server') " - :shortcut="['S']" @click=" () => { toggleMockServer(mockServer) @@ -162,7 +161,6 @@ ref="deleteAction" :icon="IconTrash2" :label="t('action.delete')" - :shortcut="['⌫']" @click=" () => { deleteMockServer(mockServer) diff --git a/packages/hoppscotch-common/src/composables/mockServerVisibility.ts b/packages/hoppscotch-common/src/composables/mockServerVisibility.ts new file mode 100644 index 00000000..02a83512 --- /dev/null +++ b/packages/hoppscotch-common/src/composables/mockServerVisibility.ts @@ -0,0 +1,45 @@ +import { computed } from "vue" +import { useSetting } from "~/composables/settings" +import { platform } from "~/platform" +import { useService } from "dioc/vue" +import { WorkspaceService } from "~/services/workspace.service" + +/** + * Composable to determine mock server visibility based on experimental flags and platform configuration + */ +export function useMockServerVisibility() { + const ENABLE_EXPERIMENTAL_MOCK_SERVERS = useSetting( + "ENABLE_EXPERIMENTAL_MOCK_SERVERS" + ) + + const workspaceService = useService(WorkspaceService) + + /** + * Check if mock servers should be visible based on experimental flag and platform configuration + */ + const isMockServerVisible = computed(() => { + // First check if experimental mock servers are enabled + if (!ENABLE_EXPERIMENTAL_MOCK_SERVERS.value) { + return false + } + + // Check if platform disables mock servers in personal workspaces + const disableInPersonalWorkspace = + platform.platformFeatureFlags.disableMockServerInPersonalWorkspace ?? + false + + // If platform disables mock servers in personal workspaces and current workspace is personal, hide mock servers + if ( + disableInPersonalWorkspace && + workspaceService.currentWorkspace.value.type === "personal" + ) { + return false + } + + return true + }) + + return { + isMockServerVisible, + } +} diff --git a/packages/hoppscotch-common/src/composables/mockServerWorkspace.ts b/packages/hoppscotch-common/src/composables/mockServerWorkspace.ts index 1efa7f19..83a67149 100644 --- a/packages/hoppscotch-common/src/composables/mockServerWorkspace.ts +++ b/packages/hoppscotch-common/src/composables/mockServerWorkspace.ts @@ -3,7 +3,7 @@ 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" +import { useMockServerVisibility } from "./mockServerVisibility" /** * Composable to handle mock server state when workspace changes @@ -12,14 +12,12 @@ import { useSetting } from "./settings" */ export function useMockServerWorkspaceSync() { const workspaceService = useService(WorkspaceService) - const ENABLE_EXPERIMENTAL_MOCK_SERVERS = useSetting( - "ENABLE_EXPERIMENTAL_MOCK_SERVERS" - ) + const { isMockServerVisible } = useMockServerVisibility() const isAuthenticated = !!platform.auth.getCurrentUser() // Initial load of mock servers for the current workspace onMounted(() => { - if (!isAuthenticated || !ENABLE_EXPERIMENTAL_MOCK_SERVERS.value) return + if (!isAuthenticated || !isMockServerVisible.value) return loadMockServers().catch(() => setMockServers([])) }) @@ -27,7 +25,7 @@ export function useMockServerWorkspaceSync() { watch( () => workspaceService.currentWorkspace.value, (newWorkspace, oldWorkspace) => { - if (!isAuthenticated || !ENABLE_EXPERIMENTAL_MOCK_SERVERS.value) return + if (!isAuthenticated || !isMockServerVisible.value) return // Clear mock servers when workspace changes to prevent stale data if ( diff --git a/packages/hoppscotch-common/src/platform/index.ts b/packages/hoppscotch-common/src/platform/index.ts index cec460c4..5aec2f81 100644 --- a/packages/hoppscotch-common/src/platform/index.ts +++ b/packages/hoppscotch-common/src/platform/index.ts @@ -66,6 +66,12 @@ export type PlatformDef = { * Whether to show the A/B testing workspace switcher click login flow or not */ workspaceSwitcherLogin?: Ref + + /** + * Whether to disable mock servers in personal workspaces + * If a value is not given, then the value is assumed to be false + */ + disableMockServerInPersonalWorkspace?: boolean } limits?: LimitsPlatformDef infra?: InfraPlatformDef