feat: secret variables in environments (#3779)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
<label for="value" class="min-w-[2.5rem] font-semibold">{{
|
||||
t("environment.value")
|
||||
}}</label>
|
||||
<input
|
||||
<SmartEnvInput
|
||||
v-model="editingValue"
|
||||
type="text"
|
||||
class="input"
|
||||
@@ -154,12 +154,14 @@ const addEnvironment = async () => {
|
||||
addGlobalEnvVariable({
|
||||
key: editingName.value,
|
||||
value: editingValue.value,
|
||||
secret: false,
|
||||
})
|
||||
toast.success(`${t("environment.updated")}`)
|
||||
} else if (scope.value.type === "my-environment") {
|
||||
addEnvironmentVariable(scope.value.index, {
|
||||
key: editingName.value,
|
||||
value: editingValue.value,
|
||||
secret: false,
|
||||
})
|
||||
toast.success(`${t("environment.updated")}`)
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Environment } from "@hoppscotch/data"
|
||||
import { Environment, NonSecretEnvironment } from "@hoppscotch/data"
|
||||
import * as E from "fp-ts/Either"
|
||||
import { ref } from "vue"
|
||||
|
||||
@@ -340,13 +340,13 @@ const showImportFailedError = () => {
|
||||
|
||||
const handleImportToStore = async (
|
||||
environments: Environment[],
|
||||
globalEnv?: Environment
|
||||
globalEnv?: NonSecretEnvironment
|
||||
) => {
|
||||
// if there's a global env, add them to the store
|
||||
if (globalEnv) {
|
||||
globalEnv.variables.forEach(({ key, value }) => {
|
||||
addGlobalEnvVariable({ key, value })
|
||||
})
|
||||
globalEnv.variables.forEach(({ key, value, secret }) =>
|
||||
addGlobalEnvVariable({ key, value, secret })
|
||||
)
|
||||
}
|
||||
|
||||
if (props.environmentType === "MY_ENV") {
|
||||
|
||||
@@ -210,7 +210,10 @@
|
||||
{{ variable.key }}
|
||||
</span>
|
||||
<span class="min-w-[9rem] w-full truncate text-secondaryLight">
|
||||
{{ variable.value }}
|
||||
<template v-if="variable.secret"> ******** </template>
|
||||
<template v-else>
|
||||
{{ variable.value }}
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="globalEnvs.length === 0" class="text-secondaryLight">
|
||||
@@ -265,7 +268,10 @@
|
||||
{{ variable.key }}
|
||||
</span>
|
||||
<span class="min-w-[9rem] w-full truncate text-secondaryLight">
|
||||
{{ variable.value }}
|
||||
<template v-if="variable.secret"> ******** </template>
|
||||
<template v-else>
|
||||
{{ variable.value }}
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@@ -479,15 +485,20 @@ const selectedEnv = computed(() => {
|
||||
type: "MY_ENV",
|
||||
index: props.modelValue.index,
|
||||
name: props.modelValue.environment?.name,
|
||||
variables: props.modelValue.environment?.variables,
|
||||
}
|
||||
} else if (props.modelValue?.type === "team-environment") {
|
||||
return {
|
||||
type: "TEAM_ENV",
|
||||
name: props.modelValue.environment.environment.name,
|
||||
teamEnvID: props.modelValue.environment.id,
|
||||
variables: props.modelValue.environment.environment.variables,
|
||||
}
|
||||
}
|
||||
return { type: "global", name: "Global" }
|
||||
return {
|
||||
type: "global",
|
||||
name: "Global",
|
||||
}
|
||||
}
|
||||
if (selectedEnvironmentIndex.value.type === "MY_ENV") {
|
||||
const environment =
|
||||
@@ -582,9 +593,7 @@ const environmentVariables = computed(() => {
|
||||
})
|
||||
|
||||
const editGlobalEnv = () => {
|
||||
invokeAction("modals.my.environment.edit", {
|
||||
envName: "Global",
|
||||
})
|
||||
invokeAction("modals.global.environment.update", {})
|
||||
}
|
||||
|
||||
const editEnv = () => {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
:action="action"
|
||||
:editing-environment-index="editingEnvironmentIndex"
|
||||
:editing-variable-name="editingVariableName"
|
||||
:env-vars="envVars"
|
||||
:is-secret-option-selected="secretOptionSelected"
|
||||
@hide-modal="displayModalEdit(false)"
|
||||
/>
|
||||
<EnvironmentsAdd
|
||||
@@ -37,7 +39,7 @@
|
||||
|
||||
<HoppSmartConfirmModal
|
||||
:show="showConfirmRemoveEnvModal"
|
||||
:title="t('confirm.remove_team')"
|
||||
:title="`${t('confirm.remove_environment')}`"
|
||||
@hide-modal="showConfirmRemoveEnvModal = false"
|
||||
@resolve="removeSelectedEnvironment()"
|
||||
/>
|
||||
@@ -67,6 +69,7 @@ import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironme
|
||||
import { useToast } from "~/composables/toast"
|
||||
import { WorkspaceService } from "~/services/workspace.service"
|
||||
import { useService } from "dioc/vue"
|
||||
import { Environment } from "@hoppscotch/data"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
@@ -88,6 +91,8 @@ const environmentType = ref<EnvironmentsChooseType>({
|
||||
const globalEnv = useReadonlyStream(globalEnv$, [])
|
||||
|
||||
const globalEnvironment = computed(() => ({
|
||||
v: 1 as const,
|
||||
id: "Global",
|
||||
name: "Global",
|
||||
variables: globalEnv.value,
|
||||
}))
|
||||
@@ -186,6 +191,7 @@ const action = ref<"new" | "edit">("edit")
|
||||
const editingEnvironmentIndex = ref<"Global" | null>(null)
|
||||
const editingVariableName = ref("")
|
||||
const editingVariableValue = ref("")
|
||||
const secretOptionSelected = ref(false)
|
||||
|
||||
const position = ref({ top: 0, left: 0 })
|
||||
|
||||
@@ -203,6 +209,7 @@ const displayModalEdit = (shouldDisplay: boolean) => {
|
||||
const editEnvironment = (environmentIndex: "Global") => {
|
||||
editingEnvironmentIndex.value = environmentIndex
|
||||
action.value = "edit"
|
||||
editingVariableName.value = ""
|
||||
displayModalEdit(true)
|
||||
}
|
||||
|
||||
@@ -232,6 +239,9 @@ const removeSelectedEnvironment = () => {
|
||||
|
||||
const resetSelectedData = () => {
|
||||
editingEnvironmentIndex.value = null
|
||||
editingVariableName.value = ""
|
||||
editingVariableValue.value = ""
|
||||
secretOptionSelected.value = false
|
||||
}
|
||||
|
||||
defineActionHandler("modals.environment.new", () => {
|
||||
@@ -243,11 +253,19 @@ defineActionHandler("modals.environment.delete-selected", () => {
|
||||
showConfirmRemoveEnvModal.value = true
|
||||
})
|
||||
|
||||
const additionalVars = ref<Environment["variables"]>([])
|
||||
|
||||
const envVars = () => [...globalEnv.value, ...additionalVars.value]
|
||||
|
||||
defineActionHandler(
|
||||
"modals.my.environment.edit",
|
||||
({ envName, variableName }) => {
|
||||
if (variableName) editingVariableName.value = variableName
|
||||
envName === "Global" && editEnvironment("Global")
|
||||
"modals.global.environment.update",
|
||||
({ variables, isSecret }) => {
|
||||
if (variables) {
|
||||
additionalVars.value = variables
|
||||
}
|
||||
secretOptionSelected.value = isSecret ?? false
|
||||
editEnvironment("Global")
|
||||
editingVariableName.value = "Global"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -16,76 +16,103 @@
|
||||
@submit="saveEnvironment"
|
||||
/>
|
||||
|
||||
<div class="flex flex-1 items-center justify-between">
|
||||
<label for="variableList" class="p-4">
|
||||
{{ t("environment.variable_list") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.clear_all')"
|
||||
:icon="clearIcon"
|
||||
@click="clearContent()"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:icon="IconPlus"
|
||||
:title="t('add.new')"
|
||||
@click="addEnvironmentVariable"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="evnExpandError"
|
||||
class="mb-2 w-full overflow-auto whitespace-normal rounded bg-primaryLight px-4 py-2 font-mono text-red-400"
|
||||
>
|
||||
{{ t("environment.nested_overflow") }}
|
||||
</div>
|
||||
<div class="divide-y divide-dividerLight rounded border border-divider">
|
||||
<div class="my-4 flex flex-col border border-divider rounded">
|
||||
<div
|
||||
v-for="({ id, env }, index) in vars"
|
||||
:key="`variable-${id}-${index}`"
|
||||
class="flex divide-x divide-dividerLight"
|
||||
v-if="evnExpandError"
|
||||
class="mb-2 w-full overflow-auto whitespace-normal rounded bg-primaryLight px-4 py-2 font-mono text-red-400"
|
||||
>
|
||||
<input
|
||||
v-model="env.key"
|
||||
v-focus
|
||||
class="flex flex-1 bg-transparent px-4 py-2"
|
||||
:placeholder="`${t('count.variable', { count: index + 1 })}`"
|
||||
:name="'param' + index"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-model="env.value"
|
||||
:select-text-on-mount="env.key === editingVariableName"
|
||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||
:envs="liveEnvs"
|
||||
:name="'value' + index"
|
||||
/>
|
||||
<div class="flex">
|
||||
<HoppButtonSecondary
|
||||
id="variable"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.remove')"
|
||||
:icon="IconTrash"
|
||||
color="red"
|
||||
@click="removeEnvironmentVariable(index)"
|
||||
/>
|
||||
</div>
|
||||
{{ t("environment.nested_overflow") }}
|
||||
</div>
|
||||
<HoppSmartPlaceholder
|
||||
v-if="vars.length === 0"
|
||||
:src="`/images/states/${colorMode.value}/blockchain.svg`"
|
||||
:alt="`${t('empty.environments')}`"
|
||||
:text="t('empty.environments')"
|
||||
>
|
||||
<template #body>
|
||||
<HoppButtonSecondary
|
||||
:label="`${t('add.new')}`"
|
||||
filled
|
||||
@click="addEnvironmentVariable"
|
||||
/>
|
||||
<HoppSmartTabs v-model="selectedEnvOption" render-inactive-tabs>
|
||||
<template #actions>
|
||||
<div class="flex flex-1 items-center justify-between">
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/documentation/features/environments"
|
||||
blank
|
||||
:title="t('app.wiki')"
|
||||
:icon="IconHelpCircle"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.clear_all')"
|
||||
:icon="clearIcon"
|
||||
@click="clearContent()"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:icon="IconPlus"
|
||||
:title="t('add.new')"
|
||||
@click="addEnvironmentVariable"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</HoppSmartPlaceholder>
|
||||
|
||||
<HoppSmartTab
|
||||
v-for="tab in tabsData"
|
||||
:id="tab.id"
|
||||
:key="tab.id"
|
||||
:label="tab.label"
|
||||
>
|
||||
<div
|
||||
class="divide-y divide-dividerLight rounded border border-divider"
|
||||
>
|
||||
<HoppSmartPlaceholder
|
||||
v-if="tab.variables.length === 0"
|
||||
:src="`/images/states/${colorMode.value}/blockchain.svg`"
|
||||
:alt="tab.emptyStateLabel"
|
||||
:text="tab.emptyStateLabel"
|
||||
>
|
||||
<template #body>
|
||||
<HoppButtonSecondary
|
||||
:label="`${t('add.new')}`"
|
||||
filled
|
||||
:icon="IconPlus"
|
||||
@click="addEnvironmentVariable"
|
||||
/>
|
||||
</template>
|
||||
</HoppSmartPlaceholder>
|
||||
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="({ id, env }, index) in tab.variables"
|
||||
:key="`variable-${id}-${index}`"
|
||||
class="flex divide-x divide-dividerLight"
|
||||
>
|
||||
<input
|
||||
v-model="env.key"
|
||||
v-focus
|
||||
class="flex flex-1 bg-transparent px-4 py-2"
|
||||
:placeholder="`${t('count.variable', {
|
||||
count: index + 1,
|
||||
})}`"
|
||||
:name="'param' + index"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-model="env.value"
|
||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||
:envs="liveEnvs"
|
||||
:name="'value' + index"
|
||||
:secret="tab.isSecret"
|
||||
:select-text-on-mount="
|
||||
env.key ? env.key === editingVariableName : false
|
||||
"
|
||||
/>
|
||||
<div class="flex">
|
||||
<HoppButtonSecondary
|
||||
id="variable"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.remove')"
|
||||
:icon="IconTrash"
|
||||
color="red"
|
||||
@click="removeEnvironmentVariable(id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</HoppSmartTab>
|
||||
</HoppSmartTabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -112,8 +139,8 @@ import IconTrash2 from "~icons/lucide/trash-2"
|
||||
import IconDone from "~icons/lucide/check"
|
||||
import IconPlus from "~icons/lucide/plus"
|
||||
import IconTrash from "~icons/lucide/trash"
|
||||
import { clone } from "lodash-es"
|
||||
import { computed, ref, watch } from "vue"
|
||||
import IconHelpCircle from "~icons/lucide/help-circle"
|
||||
import { ComputedRef, computed, ref, watch } from "vue"
|
||||
import * as E from "fp-ts/Either"
|
||||
import * as A from "fp-ts/Array"
|
||||
import * as O from "fp-ts/Option"
|
||||
@@ -136,12 +163,16 @@ import { useReadonlyStream } from "@composables/stream"
|
||||
import { useColorMode } from "@composables/theming"
|
||||
import { environmentsStore } from "~/newstore/environments"
|
||||
import { platform } from "~/platform"
|
||||
import { useService } from "dioc/vue"
|
||||
import { SecretEnvironmentService } from "~/services/secret-environment.service"
|
||||
import { uniqueId } from "lodash-es"
|
||||
|
||||
type EnvironmentVariable = {
|
||||
id: number
|
||||
env: {
|
||||
key: string
|
||||
value: string
|
||||
key: string
|
||||
secret: boolean
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +186,7 @@ const props = withDefaults(
|
||||
action: "edit" | "new"
|
||||
editingEnvironmentIndex?: number | "Global" | null
|
||||
editingVariableName?: string | null
|
||||
isSecretOptionSelected?: boolean
|
||||
envVars?: () => Environment["variables"]
|
||||
}>(),
|
||||
{
|
||||
@@ -162,6 +194,7 @@ const props = withDefaults(
|
||||
action: "edit",
|
||||
editingEnvironmentIndex: null,
|
||||
editingVariableName: null,
|
||||
isSecretOptionSelected: false,
|
||||
envVars: () => [],
|
||||
}
|
||||
)
|
||||
@@ -172,11 +205,55 @@ const emit = defineEmits<{
|
||||
|
||||
const idTicker = ref(0)
|
||||
|
||||
const tabsData: ComputedRef<
|
||||
{
|
||||
id: string
|
||||
label: string
|
||||
emptyStateLabel: string
|
||||
isSecret: boolean
|
||||
variables: EnvironmentVariable[]
|
||||
}[]
|
||||
> = computed(() => {
|
||||
return [
|
||||
{
|
||||
id: "variables",
|
||||
label: t("environment.variables"),
|
||||
emptyStateLabel: t("empty.environments"),
|
||||
isSecret: false,
|
||||
variables: nonSecretVars.value,
|
||||
},
|
||||
{
|
||||
id: "secret",
|
||||
label: t("environment.secret"),
|
||||
emptyStateLabel: t("empty.secret_environments"),
|
||||
isSecret: true,
|
||||
variables: secretVars.value,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const editingName = ref<string | null>(null)
|
||||
const editingID = ref<string>("")
|
||||
const vars = ref<EnvironmentVariable[]>([
|
||||
{ id: idTicker.value++, env: { key: "", value: "" } },
|
||||
{ id: idTicker.value++, env: { key: "", value: "", secret: false } },
|
||||
])
|
||||
|
||||
const secretEnvironmentService = useService(SecretEnvironmentService)
|
||||
|
||||
const secretVars = computed(() =>
|
||||
pipe(
|
||||
vars.value,
|
||||
A.filter((e) => e.env.secret)
|
||||
)
|
||||
)
|
||||
|
||||
const nonSecretVars = computed(() =>
|
||||
pipe(
|
||||
vars.value,
|
||||
A.filter((e) => !e.env.secret)
|
||||
)
|
||||
)
|
||||
|
||||
const clearIcon = refAutoReset<typeof IconTrash2 | typeof IconDone>(
|
||||
IconTrash2,
|
||||
1000
|
||||
@@ -184,14 +261,23 @@ const clearIcon = refAutoReset<typeof IconTrash2 | typeof IconDone>(
|
||||
|
||||
const globalVars = useReadonlyStream(globalEnv$, [])
|
||||
|
||||
type SelectedEnv = "variables" | "secret"
|
||||
|
||||
const selectedEnvOption = ref<SelectedEnv>("variables")
|
||||
|
||||
const workingEnv = computed(() => {
|
||||
if (props.editingEnvironmentIndex === "Global") {
|
||||
const vars =
|
||||
props.editingVariableName === "Global"
|
||||
? props.envVars()
|
||||
: getGlobalVariables()
|
||||
return {
|
||||
name: "Global",
|
||||
variables: getGlobalVariables(),
|
||||
variables: vars,
|
||||
} as Environment
|
||||
} else if (props.action === "new") {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
name: "",
|
||||
variables: props.envVars(),
|
||||
}
|
||||
@@ -214,6 +300,7 @@ const evnExpandError = computed(() => {
|
||||
|
||||
return pipe(
|
||||
variables,
|
||||
A.filter(({ secret }) => !secret),
|
||||
A.exists(({ value }) => E.isLeft(parseTemplateStringE(value, variables)))
|
||||
)
|
||||
})
|
||||
@@ -239,11 +326,29 @@ watch(
|
||||
(show) => {
|
||||
if (show) {
|
||||
editingName.value = workingEnv.value?.name ?? null
|
||||
selectedEnvOption.value = props.isSecretOptionSelected
|
||||
? "secret"
|
||||
: "variables"
|
||||
|
||||
if (props.editingEnvironmentIndex !== "Global") {
|
||||
editingID.value = workingEnv.value?.id ?? uniqueId()
|
||||
}
|
||||
vars.value = pipe(
|
||||
workingEnv.value?.variables ?? [],
|
||||
A.map((e) => ({
|
||||
A.mapWithIndex((index, e) => ({
|
||||
id: idTicker.value++,
|
||||
env: clone(e),
|
||||
env: {
|
||||
key: e.key,
|
||||
value: e.secret
|
||||
? secretEnvironmentService.getSecretEnvironmentVariable(
|
||||
props.editingEnvironmentIndex === "Global"
|
||||
? "Global"
|
||||
: workingEnv.value?.id,
|
||||
index
|
||||
)?.value ?? ""
|
||||
: e.value,
|
||||
secret: e.secret,
|
||||
},
|
||||
}))
|
||||
)
|
||||
}
|
||||
@@ -251,7 +356,10 @@ watch(
|
||||
)
|
||||
|
||||
const clearContent = () => {
|
||||
vars.value = []
|
||||
vars.value = vars.value.filter((e) =>
|
||||
selectedEnvOption.value === "secret" ? !e.env.secret : e.env.secret
|
||||
)
|
||||
|
||||
clearIcon.value = IconDone
|
||||
toast.success(`${t("state.cleared")}`)
|
||||
}
|
||||
@@ -262,12 +370,16 @@ const addEnvironmentVariable = () => {
|
||||
env: {
|
||||
key: "",
|
||||
value: "",
|
||||
secret: selectedEnvOption.value === "secret",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const removeEnvironmentVariable = (index: number) => {
|
||||
vars.value.splice(index, 1)
|
||||
const removeEnvironmentVariable = (id: number) => {
|
||||
const index = vars.value.findIndex((e) => e.id === id)
|
||||
if (index !== -1) {
|
||||
vars.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const saveEnvironment = () => {
|
||||
@@ -276,7 +388,7 @@ const saveEnvironment = () => {
|
||||
return
|
||||
}
|
||||
|
||||
const filterdVariables = pipe(
|
||||
const filteredVariables = pipe(
|
||||
vars.value,
|
||||
A.filterMap(
|
||||
flow(
|
||||
@@ -286,14 +398,43 @@ const saveEnvironment = () => {
|
||||
)
|
||||
)
|
||||
|
||||
const secretVariables = pipe(
|
||||
filteredVariables,
|
||||
A.filterMapWithIndex((i, e) =>
|
||||
e.secret ? O.some({ key: e.key, value: e.value, varIndex: i }) : O.none
|
||||
)
|
||||
)
|
||||
|
||||
if (editingID.value) {
|
||||
secretEnvironmentService.addSecretEnvironment(
|
||||
editingID.value,
|
||||
secretVariables
|
||||
)
|
||||
} else if (props.editingEnvironmentIndex === "Global") {
|
||||
secretEnvironmentService.addSecretEnvironment("Global", secretVariables)
|
||||
}
|
||||
|
||||
const variables = pipe(
|
||||
filteredVariables,
|
||||
A.map((e) =>
|
||||
e.secret ? { key: e.key, secret: e.secret, value: undefined } : e
|
||||
)
|
||||
)
|
||||
|
||||
const environmentUpdated: Environment = {
|
||||
v: 1,
|
||||
id: uniqueId(),
|
||||
name: editingName.value,
|
||||
variables: filterdVariables,
|
||||
variables,
|
||||
}
|
||||
|
||||
if (props.action === "new") {
|
||||
// Creating a new environment
|
||||
createEnvironment(editingName.value, environmentUpdated.variables)
|
||||
createEnvironment(
|
||||
editingName.value,
|
||||
environmentUpdated.variables,
|
||||
editingID.value
|
||||
)
|
||||
setSelectedEnvironmentIndex({
|
||||
type: "MY_ENV",
|
||||
index: envList.value.length - 1,
|
||||
@@ -332,6 +473,7 @@ const saveEnvironment = () => {
|
||||
|
||||
const hideModal = () => {
|
||||
editingName.value = null
|
||||
selectedEnvOption.value = "variables"
|
||||
emit("hide-modal")
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -135,6 +135,8 @@ import { useToast } from "@composables/toast"
|
||||
import { TippyComponent } from "vue-tippy"
|
||||
import { HoppSmartItem } from "@hoppscotch/ui"
|
||||
import { exportAsJSON } from "~/helpers/import-export/export/environment"
|
||||
import { useService } from "dioc/vue"
|
||||
import { SecretEnvironmentService } from "~/services/secret-environment.service"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
@@ -150,6 +152,8 @@ const emit = defineEmits<{
|
||||
|
||||
const confirmRemove = ref(false)
|
||||
|
||||
const secretEnvironmentService = useService(SecretEnvironmentService)
|
||||
|
||||
const exportEnvironmentAsJSON = () => {
|
||||
const { environment, environmentIndex } = props
|
||||
exportAsJSON(environment, environmentIndex)
|
||||
@@ -168,6 +172,7 @@ const removeEnvironment = () => {
|
||||
if (props.environmentIndex === null) return
|
||||
if (props.environmentIndex !== "Global") {
|
||||
deleteEnvironment(props.environmentIndex, props.environment.id)
|
||||
secretEnvironmentService.deleteSecretEnvironment(props.environment.id)
|
||||
}
|
||||
toast.success(`${t("state.deleted")}`)
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
:action="action"
|
||||
:editing-environment-index="editingEnvironmentIndex"
|
||||
:editing-variable-name="editingVariableName"
|
||||
:is-secret-option-selected="secretOptionSelected"
|
||||
@hide-modal="displayModalEdit(false)"
|
||||
/>
|
||||
<EnvironmentsImportExport
|
||||
@@ -99,6 +100,7 @@ const showModalDetails = ref(false)
|
||||
const action = ref<"new" | "edit">("edit")
|
||||
const editingEnvironmentIndex = ref<number | null>(null)
|
||||
const editingVariableName = ref("")
|
||||
const secretOptionSelected = ref(false)
|
||||
|
||||
const displayModalAdd = (shouldDisplay: boolean) => {
|
||||
action.value = "new"
|
||||
@@ -120,18 +122,23 @@ const editEnvironment = (environmentIndex: number) => {
|
||||
}
|
||||
const resetSelectedData = () => {
|
||||
editingEnvironmentIndex.value = null
|
||||
editingVariableName.value = ""
|
||||
secretOptionSelected.value = false
|
||||
}
|
||||
|
||||
defineActionHandler(
|
||||
"modals.my.environment.edit",
|
||||
({ envName, variableName }) => {
|
||||
({ envName, variableName, isSecret }) => {
|
||||
if (variableName) editingVariableName.value = variableName
|
||||
const envIndex: number = environments.value.findIndex(
|
||||
(environment: Environment) => {
|
||||
return environment.name === envName
|
||||
}
|
||||
)
|
||||
if (envName !== "Global") editEnvironment(envIndex)
|
||||
if (envName !== "Global") {
|
||||
editEnvironment(envIndex)
|
||||
secretOptionSelected.value = isSecret ?? false
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -16,90 +16,112 @@
|
||||
@submit="saveEnvironment"
|
||||
/>
|
||||
|
||||
<div class="flex flex-1 items-center justify-between">
|
||||
<label for="variableList" class="p-4">
|
||||
{{ t("environment.variable_list") }}
|
||||
</label>
|
||||
<div v-if="!isViewer" class="flex">
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.clear_all')"
|
||||
:icon="clearIcon"
|
||||
@click="clearContent()"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:icon="IconPlus"
|
||||
:title="t('add.new')"
|
||||
@click="addEnvironmentVariable"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="evnExpandError"
|
||||
class="mb-2 w-full overflow-auto whitespace-normal rounded bg-primaryLight px-4 py-2 font-mono text-red-400"
|
||||
>
|
||||
{{ t("environment.nested_overflow") }}
|
||||
</div>
|
||||
<div class="divide-y divide-dividerLight rounded border border-divider">
|
||||
<div class="my-4 flex flex-col border border-divider rounded">
|
||||
<div
|
||||
v-for="({ id, env }, index) in vars"
|
||||
:key="`variable-${id}-${index}`"
|
||||
class="flex divide-x divide-dividerLight"
|
||||
v-if="evnExpandError"
|
||||
class="mb-2 w-full overflow-auto whitespace-normal rounded bg-primaryLight px-4 py-2 font-mono text-red-400"
|
||||
>
|
||||
<input
|
||||
v-model="env.key"
|
||||
v-focus
|
||||
class="flex flex-1 bg-transparent px-4 py-2"
|
||||
:class="isViewer && 'opacity-25'"
|
||||
:placeholder="`${t('count.variable', { count: index + 1 })}`"
|
||||
:name="'param' + index"
|
||||
:disabled="isViewer"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-model="env.value"
|
||||
:select-text-on-mount="env.key === editingVariableName"
|
||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||
:envs="liveEnvs"
|
||||
:name="'value' + index"
|
||||
:readonly="isViewer"
|
||||
/>
|
||||
<div v-if="!isViewer" class="flex">
|
||||
<HoppButtonSecondary
|
||||
id="variable"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.remove')"
|
||||
:icon="IconTrash"
|
||||
color="red"
|
||||
@click="removeEnvironmentVariable(index)"
|
||||
/>
|
||||
</div>
|
||||
{{ t("environment.nested_overflow") }}
|
||||
</div>
|
||||
<HoppSmartPlaceholder
|
||||
v-if="vars.length === 0"
|
||||
:src="`/images/states/${colorMode.value}/blockchain.svg`"
|
||||
:alt="`${t('empty.environments')}`"
|
||||
:text="t('empty.environments')"
|
||||
>
|
||||
<template #body>
|
||||
<HoppButtonSecondary
|
||||
v-if="isViewer"
|
||||
disabled
|
||||
:label="`${t('add.new')}`"
|
||||
filled
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-else
|
||||
:label="`${t('add.new')}`"
|
||||
filled
|
||||
@click="addEnvironmentVariable"
|
||||
/>
|
||||
<HoppSmartTabs v-model="selectedEnvOption" render-inactive-tabs>
|
||||
<template #actions>
|
||||
<div class="flex flex-1 items-center justify-between">
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/documentation/features/environments"
|
||||
blank
|
||||
:title="t('app.wiki')"
|
||||
:icon="IconHelpCircle"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-if="!isViewer"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.clear_all')"
|
||||
:icon="clearIcon"
|
||||
@click="clearContent()"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-if="!isViewer"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:icon="IconPlus"
|
||||
:title="t('add.new')"
|
||||
@click="addEnvironmentVariable"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</HoppSmartPlaceholder>
|
||||
|
||||
<HoppSmartTab
|
||||
v-for="tab in tabsData"
|
||||
:id="tab.id"
|
||||
:key="tab.id"
|
||||
:label="tab.label"
|
||||
>
|
||||
<div
|
||||
class="divide-y divide-dividerLight rounded border border-divider"
|
||||
>
|
||||
<HoppSmartPlaceholder
|
||||
v-if="tab.variables.length === 0"
|
||||
:src="`/images/states/${colorMode.value}/blockchain.svg`"
|
||||
:alt="tab.emptyStateLabel"
|
||||
:text="tab.emptyStateLabel"
|
||||
>
|
||||
<template #body>
|
||||
<HoppButtonSecondary
|
||||
v-if="!isViewer"
|
||||
:label="`${t('add.new')}`"
|
||||
filled
|
||||
:icon="IconPlus"
|
||||
@click="addEnvironmentVariable"
|
||||
/>
|
||||
</template>
|
||||
</HoppSmartPlaceholder>
|
||||
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="({ id, env }, index) in tab.variables"
|
||||
:key="`variable-${id}-${index}`"
|
||||
class="flex divide-x divide-dividerLight"
|
||||
>
|
||||
<input
|
||||
v-model="env.key"
|
||||
v-focus
|
||||
class="flex flex-1 bg-transparent px-4 py-2"
|
||||
:placeholder="`${t('count.variable', {
|
||||
count: index + 1,
|
||||
})}`"
|
||||
:name="'param' + index"
|
||||
:disabled="isViewer"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-model="env.value"
|
||||
:select-text-on-mount="
|
||||
env.key ? env.key === editingVariableName : false
|
||||
"
|
||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||
:envs="liveEnvs"
|
||||
:name="'value' + index"
|
||||
:secret="tab.isSecret"
|
||||
:readonly="isViewer && !tab.isSecret"
|
||||
/>
|
||||
<div v-if="!isViewer" class="flex">
|
||||
<HoppButtonSecondary
|
||||
id="variable"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.remove')"
|
||||
:icon="IconTrash"
|
||||
color="red"
|
||||
@click="removeEnvironmentVariable(id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</HoppSmartTab>
|
||||
</HoppSmartTabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="!isViewer" #footer>
|
||||
<template #footer>
|
||||
<span class="flex space-x-2">
|
||||
<HoppButtonPrimary
|
||||
:label="`${t('action.save')}`"
|
||||
@@ -119,7 +141,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue"
|
||||
import { ComputedRef, computed, ref, watch } from "vue"
|
||||
import * as E from "fp-ts/Either"
|
||||
import * as A from "fp-ts/Array"
|
||||
import * as O from "fp-ts/Option"
|
||||
@@ -141,13 +163,17 @@ import IconTrash from "~icons/lucide/trash"
|
||||
import IconTrash2 from "~icons/lucide/trash-2"
|
||||
import IconDone from "~icons/lucide/check"
|
||||
import IconPlus from "~icons/lucide/plus"
|
||||
import IconHelpCircle from "~icons/lucide/help-circle"
|
||||
import { platform } from "~/platform"
|
||||
import { useService } from "dioc/vue"
|
||||
import { SecretEnvironmentService } from "~/services/secret-environment.service"
|
||||
|
||||
type EnvironmentVariable = {
|
||||
id: number
|
||||
env: {
|
||||
key: string
|
||||
value: string
|
||||
secret: boolean
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +189,7 @@ const props = withDefaults(
|
||||
editingTeamId: string | undefined
|
||||
editingVariableName?: string | null
|
||||
isViewer?: boolean
|
||||
isSecretOptionSelected?: boolean
|
||||
envVars?: () => Environment["variables"]
|
||||
}>(),
|
||||
{
|
||||
@@ -172,6 +199,7 @@ const props = withDefaults(
|
||||
editingTeamId: "",
|
||||
editingVariableName: null,
|
||||
isViewer: false,
|
||||
isSecretOptionSelected: false,
|
||||
envVars: () => [],
|
||||
}
|
||||
)
|
||||
@@ -182,11 +210,59 @@ const emit = defineEmits<{
|
||||
|
||||
const idTicker = ref(0)
|
||||
|
||||
const tabsData: ComputedRef<
|
||||
{
|
||||
id: string
|
||||
label: string
|
||||
emptyStateLabel: string
|
||||
isSecret: boolean
|
||||
variables: EnvironmentVariable[]
|
||||
}[]
|
||||
> = computed(() => {
|
||||
return [
|
||||
{
|
||||
id: "variables",
|
||||
label: t("environment.variables"),
|
||||
emptyStateLabel: t("empty.environments"),
|
||||
isSecret: false,
|
||||
variables: nonSecretVars.value,
|
||||
},
|
||||
{
|
||||
id: "secret",
|
||||
label: t("environment.secret"),
|
||||
emptyStateLabel: t("empty.secret_environments"),
|
||||
isSecret: true,
|
||||
variables: secretVars.value,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const editingName = ref<string | null>(null)
|
||||
const editingID = ref<string | null>(null)
|
||||
const vars = ref<EnvironmentVariable[]>([
|
||||
{ id: idTicker.value++, env: { key: "", value: "" } },
|
||||
{ id: idTicker.value++, env: { key: "", value: "", secret: false } },
|
||||
])
|
||||
|
||||
const secretEnvironmentService = useService(SecretEnvironmentService)
|
||||
|
||||
const secretVars = computed(() =>
|
||||
pipe(
|
||||
vars.value,
|
||||
A.filter((e) => e.env.secret)
|
||||
)
|
||||
)
|
||||
|
||||
const nonSecretVars = computed(() =>
|
||||
pipe(
|
||||
vars.value,
|
||||
A.filter((e) => !e.env.secret)
|
||||
)
|
||||
)
|
||||
|
||||
type SelectedEnv = "variables" | "secret"
|
||||
|
||||
const selectedEnvOption = ref<SelectedEnv>("variables")
|
||||
|
||||
const clearIcon = refAutoReset<typeof IconTrash2 | typeof IconDone>(
|
||||
IconTrash2,
|
||||
1000
|
||||
@@ -215,22 +291,34 @@ watch(
|
||||
() => props.show,
|
||||
(show) => {
|
||||
if (show) {
|
||||
editingName.value = props.editingEnvironment?.environment.name ?? null
|
||||
selectedEnvOption.value = props.isSecretOptionSelected
|
||||
? "secret"
|
||||
: "variables"
|
||||
if (props.action === "new") {
|
||||
editingName.value = null
|
||||
vars.value = pipe(
|
||||
props.envVars() ?? [],
|
||||
A.map((e: { key: string; value: string }) => ({
|
||||
A.map((e) => ({
|
||||
id: idTicker.value++,
|
||||
env: clone(e),
|
||||
}))
|
||||
)
|
||||
} else if (props.editingEnvironment !== null) {
|
||||
editingName.value = props.editingEnvironment.environment.name ?? null
|
||||
editingID.value = props.editingEnvironment.id
|
||||
vars.value = pipe(
|
||||
props.editingEnvironment.environment.variables ?? [],
|
||||
A.map((e: { key: string; value: string }) => ({
|
||||
A.mapWithIndex((index, e) => ({
|
||||
id: idTicker.value++,
|
||||
env: clone(e),
|
||||
env: {
|
||||
key: e.key,
|
||||
value: e.secret
|
||||
? secretEnvironmentService.getSecretEnvironmentVariable(
|
||||
editingID.value ?? "",
|
||||
index
|
||||
)?.value ?? ""
|
||||
: e.value,
|
||||
secret: e.secret,
|
||||
},
|
||||
}))
|
||||
)
|
||||
}
|
||||
@@ -250,12 +338,16 @@ const addEnvironmentVariable = () => {
|
||||
env: {
|
||||
key: "",
|
||||
value: "",
|
||||
secret: selectedEnvOption.value === "secret",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const removeEnvironmentVariable = (index: number) => {
|
||||
vars.value.splice(index, 1)
|
||||
const removeEnvironmentVariable = (id: number) => {
|
||||
const index = vars.value.findIndex((e) => e.id === id)
|
||||
if (index !== -1) {
|
||||
vars.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const isLoading = ref(false)
|
||||
@@ -278,52 +370,102 @@ const saveEnvironment = async () => {
|
||||
)
|
||||
)
|
||||
|
||||
const secretVariables = pipe(
|
||||
filterdVariables,
|
||||
A.filterMapWithIndex((i, e) =>
|
||||
e.secret ? O.some({ key: e.key, value: e.value, varIndex: i }) : O.none
|
||||
)
|
||||
)
|
||||
|
||||
const variables = pipe(
|
||||
filterdVariables,
|
||||
A.map((e) =>
|
||||
e.secret ? { key: e.key, secret: e.secret, value: undefined } : e
|
||||
)
|
||||
)
|
||||
|
||||
const environmentUpdated: Environment = {
|
||||
v: 1,
|
||||
id: editingID.value ?? "",
|
||||
name: editingName.value,
|
||||
variables,
|
||||
}
|
||||
|
||||
if (props.action === "new") {
|
||||
platform.analytics?.logEvent({
|
||||
type: "HOPP_CREATE_ENVIRONMENT",
|
||||
workspaceType: "team",
|
||||
})
|
||||
|
||||
await pipe(
|
||||
createTeamEnvironment(
|
||||
JSON.stringify(filterdVariables),
|
||||
props.editingTeamId,
|
||||
editingName.value
|
||||
),
|
||||
TE.match(
|
||||
(err: GQLError<string>) => {
|
||||
console.error(err)
|
||||
toast.error(`${getErrorMessage(err)}`)
|
||||
},
|
||||
() => {
|
||||
hideModal()
|
||||
toast.success(`${t("environment.created")}`)
|
||||
}
|
||||
)
|
||||
)()
|
||||
if (!props.isViewer) {
|
||||
await pipe(
|
||||
createTeamEnvironment(
|
||||
JSON.stringify(environmentUpdated.variables),
|
||||
props.editingTeamId,
|
||||
environmentUpdated.name
|
||||
),
|
||||
TE.match(
|
||||
(err: GQLError<string>) => {
|
||||
console.error(err)
|
||||
toast.error(`${getErrorMessage(err)}`)
|
||||
isLoading.value = false
|
||||
},
|
||||
(res) => {
|
||||
const envID = res.createTeamEnvironment.id
|
||||
if (envID) {
|
||||
secretEnvironmentService.addSecretEnvironment(
|
||||
envID,
|
||||
secretVariables
|
||||
)
|
||||
}
|
||||
hideModal()
|
||||
toast.success(`${t("environment.created")}`)
|
||||
isLoading.value = false
|
||||
}
|
||||
)
|
||||
)()
|
||||
}
|
||||
} else {
|
||||
if (!props.editingEnvironment) {
|
||||
console.error("No Environment Found")
|
||||
return
|
||||
}
|
||||
|
||||
await pipe(
|
||||
updateTeamEnvironment(
|
||||
JSON.stringify(filterdVariables),
|
||||
props.editingEnvironment.id,
|
||||
editingName.value
|
||||
),
|
||||
TE.match(
|
||||
(err: GQLError<string>) => {
|
||||
console.error(err)
|
||||
toast.error(`${getErrorMessage(err)}`)
|
||||
},
|
||||
() => {
|
||||
hideModal()
|
||||
toast.success(`${t("environment.updated")}`)
|
||||
}
|
||||
if (editingID.value) {
|
||||
secretEnvironmentService.addSecretEnvironment(
|
||||
editingID.value,
|
||||
secretVariables
|
||||
)
|
||||
)()
|
||||
|
||||
// If the user is a viewer, we don't need to update the environment in BE
|
||||
// just update the secret environment in the local storage
|
||||
if (props.isViewer) {
|
||||
hideModal()
|
||||
toast.success(`${t("environment.updated")}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!props.isViewer) {
|
||||
await pipe(
|
||||
updateTeamEnvironment(
|
||||
JSON.stringify(environmentUpdated.variables),
|
||||
props.editingEnvironment.id,
|
||||
environmentUpdated.name
|
||||
),
|
||||
TE.match(
|
||||
(err: GQLError<string>) => {
|
||||
console.error(err)
|
||||
toast.error(`${getErrorMessage(err)}`)
|
||||
isLoading.value = false
|
||||
},
|
||||
() => {
|
||||
hideModal()
|
||||
toast.success(`${t("environment.updated")}`)
|
||||
isLoading.value = false
|
||||
}
|
||||
)
|
||||
)()
|
||||
}
|
||||
}
|
||||
|
||||
isLoading.value = false
|
||||
@@ -331,6 +473,7 @@ const saveEnvironment = async () => {
|
||||
|
||||
const hideModal = () => {
|
||||
editingName.value = null
|
||||
selectedEnvOption.value = "variables"
|
||||
emit("hide-modal")
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
</span>
|
||||
<span>
|
||||
<tippy
|
||||
v-if="!isViewer"
|
||||
ref="options"
|
||||
interactive
|
||||
trigger="click"
|
||||
@@ -57,6 +56,7 @@
|
||||
/>
|
||||
|
||||
<HoppSmartItem
|
||||
v-if="!isViewer"
|
||||
ref="duplicate"
|
||||
:icon="IconCopy"
|
||||
:label="`${t('action.duplicate')}`"
|
||||
@@ -69,6 +69,7 @@
|
||||
"
|
||||
/>
|
||||
<HoppSmartItem
|
||||
v-if="!isViewer"
|
||||
ref="exportAsJsonEl"
|
||||
:icon="IconEdit"
|
||||
:label="`${t('export.as_json')}`"
|
||||
@@ -81,6 +82,7 @@
|
||||
"
|
||||
/>
|
||||
<HoppSmartItem
|
||||
v-if="!isViewer"
|
||||
ref="deleteAction"
|
||||
:icon="IconTrash2"
|
||||
:label="`${t('action.delete')}`"
|
||||
@@ -124,6 +126,8 @@ import IconMoreVertical from "~icons/lucide/more-vertical"
|
||||
import { TippyComponent } from "vue-tippy"
|
||||
import { HoppSmartItem } from "@hoppscotch/ui"
|
||||
import { exportAsJSON } from "~/helpers/import-export/export/environment"
|
||||
import { useService } from "dioc/vue"
|
||||
import { SecretEnvironmentService } from "~/services/secret-environment.service"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
@@ -137,6 +141,8 @@ const emit = defineEmits<{
|
||||
(e: "edit-environment"): void
|
||||
}>()
|
||||
|
||||
const secretEnvironmentService = useService(SecretEnvironmentService)
|
||||
|
||||
const confirmRemove = ref(false)
|
||||
|
||||
const exportEnvironmentAsJSON = () =>
|
||||
@@ -161,6 +167,7 @@ const removeEnvironment = () => {
|
||||
},
|
||||
() => {
|
||||
toast.success(`${t("team_environment.deleted")}`)
|
||||
secretEnvironmentService.deleteSecretEnvironment(props.environment.id)
|
||||
}
|
||||
)
|
||||
)()
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
:editing-environment="editingEnvironment"
|
||||
:editing-team-id="team?.id"
|
||||
:editing-variable-name="editingVariableName"
|
||||
:is-secret-option-selected="secretOptionSelected"
|
||||
:is-viewer="team?.myRole === 'VIEWER'"
|
||||
@hide-modal="displayModalEdit(false)"
|
||||
/>
|
||||
@@ -148,6 +149,7 @@ const showModalDetails = ref(false)
|
||||
const action = ref<"new" | "edit">("edit")
|
||||
const editingEnvironment = ref<TeamEnvironment | null>(null)
|
||||
const editingVariableName = ref("")
|
||||
const secretOptionSelected = ref(false)
|
||||
|
||||
const isTeamViewer = computed(() => props.team?.myRole === "VIEWER")
|
||||
|
||||
@@ -171,6 +173,8 @@ const editEnvironment = (environment: TeamEnvironment | null) => {
|
||||
}
|
||||
const resetSelectedData = () => {
|
||||
editingEnvironment.value = null
|
||||
editingVariableName.value = ""
|
||||
secretOptionSelected.value = false
|
||||
}
|
||||
|
||||
const getErrorMessage = (err: GQLError<string>) => {
|
||||
@@ -187,12 +191,15 @@ const getErrorMessage = (err: GQLError<string>) => {
|
||||
|
||||
defineActionHandler(
|
||||
"modals.team.environment.edit",
|
||||
({ envName, variableName }) => {
|
||||
({ envName, variableName, isSecret }) => {
|
||||
if (variableName) editingVariableName.value = variableName
|
||||
const teamEnvToEdit = props.teamEnvironments.find(
|
||||
(environment) => environment.environment.name === envName
|
||||
)
|
||||
if (teamEnvToEdit) editEnvironment(teamEnvToEdit)
|
||||
if (teamEnvToEdit) {
|
||||
editEnvironment(teamEnvToEdit)
|
||||
secretOptionSelected.value = isSecret ?? false
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user