fix(common): set domain url as mockserver environment (#6185)
This commit is contained in:
parent
c4e1f02abf
commit
50f16e2ab5
1 changed files with 161 additions and 20 deletions
|
|
@ -4,6 +4,7 @@ import { useToast } from "@composables/toast"
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import { pipe } from "fp-ts/function"
|
import { pipe } from "fp-ts/function"
|
||||||
import * as TE from "fp-ts/TaskEither"
|
import * as TE from "fp-ts/TaskEither"
|
||||||
|
import { translateToNewEnvironmentVariables } from "@hoppscotch/data"
|
||||||
import { computed } from "vue"
|
import { computed } from "vue"
|
||||||
import { WorkspaceType } from "~/helpers/backend/graphql"
|
import { WorkspaceType } from "~/helpers/backend/graphql"
|
||||||
import type { MockServer } from "~/helpers/backend/types/MockServer"
|
import type { MockServer } from "~/helpers/backend/types/MockServer"
|
||||||
|
|
@ -13,6 +14,7 @@ import {
|
||||||
updateTeamEnvironment,
|
updateTeamEnvironment,
|
||||||
} from "~/helpers/backend/mutations/TeamEnvironment"
|
} from "~/helpers/backend/mutations/TeamEnvironment"
|
||||||
import TeamEnvironmentAdapter from "~/helpers/teams/TeamEnvironmentAdapter"
|
import TeamEnvironmentAdapter from "~/helpers/teams/TeamEnvironmentAdapter"
|
||||||
|
import { uniqueID } from "~/helpers/utils/uniqueID"
|
||||||
import { restCollections$ } from "~/newstore/collections"
|
import { restCollections$ } from "~/newstore/collections"
|
||||||
import {
|
import {
|
||||||
addEnvironmentVariable,
|
addEnvironmentVariable,
|
||||||
|
|
@ -27,14 +29,36 @@ import {
|
||||||
updateMockServer as updateMockServerInStore,
|
updateMockServer as updateMockServerInStore,
|
||||||
loadMockServers,
|
loadMockServers,
|
||||||
} from "~/newstore/mockServers"
|
} from "~/newstore/mockServers"
|
||||||
|
import { CurrentValueService } from "~/services/current-environment-value.service"
|
||||||
import { TeamCollectionsService } from "~/services/team-collection.service"
|
import { TeamCollectionsService } from "~/services/team-collection.service"
|
||||||
import { WorkspaceService } from "~/services/workspace.service"
|
import { WorkspaceService } from "~/services/workspace.service"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks which mock-server URL should be stored as the `mockUrl`
|
||||||
|
* environment variable.
|
||||||
|
*
|
||||||
|
* Policy: always prefer the subdomain-based URL
|
||||||
|
* (`serverUrlDomainBased`) when it's available and fall back to the
|
||||||
|
* path-based URL (`serverUrlPathBased`) otherwise. The backend only
|
||||||
|
* returns `serverUrlDomainBased` when a wildcard domain is configured,
|
||||||
|
* so the path-based URL is the universal fallback. On the cloud
|
||||||
|
* instance only `serverUrlDomainBased` is returned, so that URL is
|
||||||
|
* used there.
|
||||||
|
*/
|
||||||
|
function pickMockUrl(
|
||||||
|
server: Pick<MockServer, "serverUrlPathBased" | "serverUrlDomainBased">
|
||||||
|
): string {
|
||||||
|
const path = server.serverUrlPathBased ?? ""
|
||||||
|
const subdomain = server.serverUrlDomainBased ?? ""
|
||||||
|
return subdomain || path
|
||||||
|
}
|
||||||
|
|
||||||
export function useMockServer() {
|
export function useMockServer() {
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const workspaceService = useService(WorkspaceService)
|
const workspaceService = useService(WorkspaceService)
|
||||||
const teamCollectionsService = useService(TeamCollectionsService)
|
const teamCollectionsService = useService(TeamCollectionsService)
|
||||||
|
const currentValueService = useService(CurrentValueService)
|
||||||
|
|
||||||
const mockServers = useReadonlyStream(mockServers$, [])
|
const mockServers = useReadonlyStream(mockServers$, [])
|
||||||
const collections = useReadonlyStream(restCollections$, [])
|
const collections = useReadonlyStream(restCollections$, [])
|
||||||
|
|
@ -93,7 +117,13 @@ export function useMockServer() {
|
||||||
const workspaceType = currentWorkspace.value.type
|
const workspaceType = currentWorkspace.value.type
|
||||||
|
|
||||||
if (workspaceType === "personal") {
|
if (workspaceType === "personal") {
|
||||||
// For personal workspace, add to selected environment or create new one
|
// For personal workspace, add to selected environment or create new one.
|
||||||
|
//
|
||||||
|
// Architectural note: env variables are split into a persisted half
|
||||||
|
// (`initialValue`, goes to the store / backend) and a local half
|
||||||
|
// (`currentValue`, stored only in CurrentValueService). The persisted
|
||||||
|
// payload must always carry `currentValue: ""`; the real value is
|
||||||
|
// registered separately via `currentValueService`.
|
||||||
const selectedEnvIndex = getSelectedEnvironmentIndex()
|
const selectedEnvIndex = getSelectedEnvironmentIndex()
|
||||||
|
|
||||||
if (selectedEnvIndex.type === "MY_ENV") {
|
if (selectedEnvIndex.type === "MY_ENV") {
|
||||||
|
|
@ -104,36 +134,72 @@ export function useMockServer() {
|
||||||
)
|
)
|
||||||
|
|
||||||
if (existingVariableIndex === -1) {
|
if (existingVariableIndex === -1) {
|
||||||
// Add to existing selected environment
|
// Add to existing selected environment. The new variable will be
|
||||||
|
// appended at `env.variables.length` once the dispatch lands.
|
||||||
|
const newVarIndex = env.variables.length
|
||||||
addEnvironmentVariable(selectedEnvIndex.index, {
|
addEnvironmentVariable(selectedEnvIndex.index, {
|
||||||
key: "mockUrl",
|
key: "mockUrl",
|
||||||
initialValue: mockUrl,
|
initialValue: mockUrl,
|
||||||
currentValue: mockUrl,
|
currentValue: "",
|
||||||
secret: false,
|
secret: false,
|
||||||
})
|
})
|
||||||
|
currentValueService.addEnvironmentVariable(env.id, {
|
||||||
|
key: "mockUrl",
|
||||||
|
currentValue: mockUrl,
|
||||||
|
varIndex: newVarIndex,
|
||||||
|
isSecret: false,
|
||||||
|
})
|
||||||
toast.success(t("mock_server.environment_variable_added"))
|
toast.success(t("mock_server.environment_variable_added"))
|
||||||
} else {
|
} else {
|
||||||
// Update existing mockUrl variable with new value using the store dispatcher
|
// Update existing mockUrl variable with new value using the
|
||||||
|
// store dispatcher. Persist initial only; update the current
|
||||||
|
// value separately via the service (remove + add, since there
|
||||||
|
// is no explicit update API on the service).
|
||||||
updateEnvironmentVariable(
|
updateEnvironmentVariable(
|
||||||
selectedEnvIndex.index,
|
selectedEnvIndex.index,
|
||||||
existingVariableIndex,
|
existingVariableIndex,
|
||||||
{
|
{
|
||||||
key: "mockUrl",
|
key: "mockUrl",
|
||||||
initialValue: mockUrl,
|
initialValue: mockUrl,
|
||||||
currentValue: mockUrl,
|
currentValue: "",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
currentValueService.removeEnvironmentVariable(
|
||||||
|
env.id,
|
||||||
|
existingVariableIndex
|
||||||
|
)
|
||||||
|
currentValueService.addEnvironmentVariable(env.id, {
|
||||||
|
key: "mockUrl",
|
||||||
|
currentValue: mockUrl,
|
||||||
|
varIndex: existingVariableIndex,
|
||||||
|
isSecret: false,
|
||||||
|
})
|
||||||
toast.success(t("mock_server.environment_variable_updated"))
|
toast.success(t("mock_server.environment_variable_updated"))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create a new environment with the mock URL
|
// Create a new environment with the mock URL.
|
||||||
|
// We generate the env ID up front so we can register the current
|
||||||
|
// value against the same ID without racing the dispatch.
|
||||||
const envName = `${collectionName} Environment`
|
const envName = `${collectionName} Environment`
|
||||||
createEnvironment(envName, [
|
const envID = uniqueID()
|
||||||
|
createEnvironment(
|
||||||
|
envName,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: "mockUrl",
|
||||||
|
initialValue: mockUrl,
|
||||||
|
currentValue: "",
|
||||||
|
secret: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
envID
|
||||||
|
)
|
||||||
|
currentValueService.addEnvironment(envID, [
|
||||||
{
|
{
|
||||||
key: "mockUrl",
|
key: "mockUrl",
|
||||||
initialValue: mockUrl,
|
|
||||||
currentValue: mockUrl,
|
currentValue: mockUrl,
|
||||||
secret: false,
|
varIndex: 0,
|
||||||
|
isSecret: false,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
toast.success(t("mock_server.environment_created_with_variable"))
|
toast.success(t("mock_server.environment_created_with_variable"))
|
||||||
|
|
@ -158,24 +224,64 @@ export function useMockServer() {
|
||||||
let updatedVariables
|
let updatedVariables
|
||||||
let successMessage
|
let successMessage
|
||||||
|
|
||||||
|
// Track the varIndex that will hold mockUrl after the update
|
||||||
|
// so we can register the current value against the right slot.
|
||||||
|
let mockUrlVarIndex: number
|
||||||
|
|
||||||
if (existingVariableIndex === -1) {
|
if (existingVariableIndex === -1) {
|
||||||
// Variable doesn't exist, add it
|
// Variable doesn't exist, append it. Team env variables follow
|
||||||
|
// the v2 schema ({ key, initialValue, currentValue, secret }).
|
||||||
|
// `currentValue` must be empty on persist — the real value is
|
||||||
|
// stored locally via CurrentValueService.
|
||||||
|
mockUrlVarIndex = existingEnv.environment.variables.length
|
||||||
updatedVariables = [
|
updatedVariables = [
|
||||||
...existingEnv.environment.variables,
|
...existingEnv.environment.variables,
|
||||||
{ key: "mockUrl", value: mockUrl },
|
{
|
||||||
|
key: "mockUrl",
|
||||||
|
initialValue: mockUrl,
|
||||||
|
currentValue: "",
|
||||||
|
secret: false,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
successMessage = t("mock_server.environment_variable_added")
|
successMessage = t("mock_server.environment_variable_added")
|
||||||
} else {
|
} else {
|
||||||
// Variable exists, update its value
|
// Variable exists, bump its initialValue; keep currentValue
|
||||||
|
// empty on persist and refresh the service entry below.
|
||||||
|
//
|
||||||
|
// We rebuild the v2 shape explicitly rather than spreading
|
||||||
|
// the existing variable — a legacy `{ key, value }` row
|
||||||
|
// would otherwise leak its `value` field alongside
|
||||||
|
// `initialValue` / `currentValue` and produce a mixed-
|
||||||
|
// schema payload.
|
||||||
|
mockUrlVarIndex = existingVariableIndex
|
||||||
updatedVariables = existingEnv.environment.variables.map((v, idx) =>
|
updatedVariables = existingEnv.environment.variables.map((v, idx) =>
|
||||||
idx === existingVariableIndex ? { ...v, value: mockUrl } : v
|
idx === existingVariableIndex
|
||||||
|
? {
|
||||||
|
key: "mockUrl",
|
||||||
|
initialValue: mockUrl,
|
||||||
|
currentValue: "",
|
||||||
|
secret: false,
|
||||||
|
}
|
||||||
|
: v
|
||||||
)
|
)
|
||||||
successMessage = t("mock_server.environment_variable_updated")
|
successMessage = t("mock_server.environment_variable_updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize every entry before persisting. Other variables
|
||||||
|
// in this list may still be legacy `{ key, value }` rows
|
||||||
|
// because `TeamEnvironmentAdapter` subscribes via raw
|
||||||
|
// `JSON.parse` without running the translator — if we just
|
||||||
|
// stringified `updatedVariables` as-is we could send a
|
||||||
|
// mixed-schema payload back to the backend. Running each
|
||||||
|
// row through `translateToNewEnvironmentVariables` guarantees
|
||||||
|
// all entries are in the v2 shape.
|
||||||
|
const normalizedVariables = updatedVariables.map(
|
||||||
|
translateToNewEnvironmentVariables
|
||||||
|
)
|
||||||
|
|
||||||
await pipe(
|
await pipe(
|
||||||
updateTeamEnvironment(
|
updateTeamEnvironment(
|
||||||
JSON.stringify(updatedVariables),
|
JSON.stringify(normalizedVariables),
|
||||||
existingEnv.id,
|
existingEnv.id,
|
||||||
existingEnv.environment.name
|
existingEnv.environment.name
|
||||||
),
|
),
|
||||||
|
|
@ -185,14 +291,36 @@ export function useMockServer() {
|
||||||
toast.error(t("error.something_went_wrong"))
|
toast.error(t("error.something_went_wrong"))
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
|
// Persist succeeded — now register the real current value
|
||||||
|
// against the team env's ID. Remove any stale entry at the
|
||||||
|
// same slot first (no explicit update API on the service).
|
||||||
|
currentValueService.removeEnvironmentVariable(
|
||||||
|
existingEnv.id,
|
||||||
|
mockUrlVarIndex
|
||||||
|
)
|
||||||
|
currentValueService.addEnvironmentVariable(existingEnv.id, {
|
||||||
|
key: "mockUrl",
|
||||||
|
currentValue: mockUrl,
|
||||||
|
varIndex: mockUrlVarIndex,
|
||||||
|
isSecret: false,
|
||||||
|
})
|
||||||
toast.success(successMessage)
|
toast.success(successMessage)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)()
|
)()
|
||||||
} else {
|
} else {
|
||||||
// Create new team environment
|
// Create new team environment. Variables go out with an empty
|
||||||
|
// currentValue; the real value is registered locally against
|
||||||
|
// the server-assigned env ID once the mutation returns.
|
||||||
const envName = `${collectionName} Environment`
|
const envName = `${collectionName} Environment`
|
||||||
const variables = [{ key: "mockUrl", value: mockUrl }]
|
const variables = [
|
||||||
|
{
|
||||||
|
key: "mockUrl",
|
||||||
|
initialValue: mockUrl,
|
||||||
|
currentValue: "",
|
||||||
|
secret: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
await pipe(
|
await pipe(
|
||||||
createTeamEnvironment(JSON.stringify(variables), teamID, envName),
|
createTeamEnvironment(JSON.stringify(variables), teamID, envName),
|
||||||
|
|
@ -201,7 +329,18 @@ export function useMockServer() {
|
||||||
console.error("Failed to create team environment:", error)
|
console.error("Failed to create team environment:", error)
|
||||||
toast.error(t("error.something_went_wrong"))
|
toast.error(t("error.something_went_wrong"))
|
||||||
},
|
},
|
||||||
() => {
|
(result) => {
|
||||||
|
const newEnvID = result.createTeamEnvironment.id
|
||||||
|
if (newEnvID) {
|
||||||
|
currentValueService.addEnvironment(newEnvID, [
|
||||||
|
{
|
||||||
|
key: "mockUrl",
|
||||||
|
currentValue: mockUrl,
|
||||||
|
varIndex: 0,
|
||||||
|
isSecret: false,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
toast.success(t("mock_server.environment_created_with_variable"))
|
toast.success(t("mock_server.environment_created_with_variable"))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -284,10 +423,12 @@ export function useMockServer() {
|
||||||
return { success: false, server: null }
|
return { success: false, server: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add mock URL to environment if enabled
|
// Add mock URL to environment if enabled.
|
||||||
|
// Always prefer `serverUrlDomainBased`; fall back to
|
||||||
|
// `serverUrlPathBased` when the backend has no wildcard domain
|
||||||
|
// configured and the subdomain URL comes back null.
|
||||||
if (setInEnvironment) {
|
if (setInEnvironment) {
|
||||||
const mockUrl =
|
const mockUrl = pickMockUrl(result)
|
||||||
result.serverUrlPathBased || result.serverUrlDomainBased || ""
|
|
||||||
if (mockUrl) {
|
if (mockUrl) {
|
||||||
await addMockUrlToEnvironment(mockUrl, collectionName)
|
await addMockUrlToEnvironment(mockUrl, collectionName)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue