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 <arif.ishan05@gmail.com>
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Anwarul Islam 2025-10-30 15:47:16 +06:00 committed by GitHub
parent c73e71827a
commit e607f9db24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 92 additions and 35 deletions

View file

@ -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);

View file

@ -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 }
}

View file

@ -52,7 +52,7 @@
/>
</HoppSmartTab>
<HoppSmartTab
v-if="ENABLE_EXPERIMENTAL_MOCK_SERVERS"
v-if="isMockServerVisible"
:id="'mock-servers'"
:icon="IconServer"
:label="`${t('tab.mock_servers')}`"
@ -76,15 +76,13 @@ import IconCode from "~icons/lucide/code"
import IconServer from "~icons/lucide/server"
import { ref } from "vue"
import { useI18n } from "@composables/i18n"
import { useSetting } from "@composables/settings"
import MockServerDashboard from "~/components/mockServer/MockServerDashboard.vue"
import { useMockServerWorkspaceSync } from "~/composables/mockServerWorkspace"
import { useMockServerVisibility } from "~/composables/mockServerVisibility"
const t = useI18n()
const ENABLE_EXPERIMENTAL_MOCK_SERVERS = useSetting(
"ENABLE_EXPERIMENTAL_MOCK_SERVERS"
)
const { isMockServerVisible } = useMockServerVisibility()
type RequestOptionTabs =
| "history"

View file

@ -134,8 +134,8 @@
class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium"
:class="
existingMockServer?.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'
"
>
<span
@ -143,7 +143,7 @@
:class="
existingMockServer?.isActive
? 'bg-green-400'
: 'bg-gray-400'
: 'bg-secondaryLight'
"
></span>
{{

View file

@ -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'
"
>
<span
class="w-2 h-2 rounded-full mr-2"
:class="isActive ? 'bg-green-400' : 'bg-gray-400'"
:class="isActive ? 'bg-green-400' : 'bg-secondaryLight'"
></span>
{{
isActive

View file

@ -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)

View file

@ -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,
}
}

View file

@ -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 (

View file

@ -66,6 +66,12 @@ export type PlatformDef = {
* Whether to show the A/B testing workspace switcher click login flow or not
*/
workspaceSwitcherLogin?: Ref<boolean>
/**
* 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