Merge pull request #15794 from Budibase/BUDI-9127/extract-env-input

Extract env input component
This commit is contained in:
Adria Navarro 2025-03-25 15:26:57 +01:00 committed by GitHub
commit 871d875c68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 208 additions and 193 deletions

View File

@ -27,5 +27,6 @@
},
"[handlebars]": {
"editor.formatOnSave": false
}
},
"eslint.validate": ["javascript", "typescript", "svelte"]
}

View File

@ -1,25 +1,26 @@
<script>
<script lang="ts">
import "@spectrum-css/textfield/dist/index-vars.css"
import { createEventDispatcher, onMount } from "svelte"
import clickOutside from "../../Actions/click_outside"
import Divider from "../../Divider/Divider.svelte"
import type { EnvDropdownType } from "../../types"
export let value = null
export let placeholder = null
export let type = "text"
export let disabled = false
export let id = null
export let readonly = false
export let updateOnChange = true
export let align
export let autofocus = false
export let value: string | number | undefined = undefined
export let placeholder: string | undefined = undefined
export let type: EnvDropdownType = "text"
export let disabled: boolean = false
export let id: string | undefined = undefined
export let readonly: boolean = false
export let updateOnChange: boolean = true
export let align: string | undefined = undefined
export let autofocus: boolean = false
export let variables
export let showModal
export let showModal: () => void
export let environmentVariablesEnabled
export let handleUpgradePanel
export let handleUpgradePanel: () => void
const dispatch = createEventDispatcher()
let field
let field: HTMLInputElement
let focus = false
let iconFocused = false
let open = false
@ -30,7 +31,7 @@
// Strips the name out of the value which is {{ env.Variable }} resulting in an array like ["Variable"]
$: hbsValue = String(value)?.match(STRIP_NAME_REGEX) || []
const updateValue = newValue => {
const updateValue = (newValue: any) => {
if (readonly) {
return
}
@ -48,7 +49,7 @@
focus = true
}
const onBlur = event => {
const onBlur = (event: any) => {
if (readonly) {
return
}
@ -56,14 +57,14 @@
updateValue(event.target.value)
}
const onInput = event => {
const onInput = (event: any) => {
if (readonly || !updateOnChange) {
return
}
updateValue(event.target.value)
}
const handleOutsideClick = event => {
const handleOutsideClick = (event: Event) => {
if (open) {
event.stopPropagation()
open = false
@ -73,7 +74,7 @@
}
}
const handleVarSelect = variable => {
const handleVarSelect = (variable: string) => {
open = false
focus = false
iconFocused = false
@ -121,10 +122,10 @@
<input
bind:this={field}
disabled={hbsValue.length || disabled}
disabled={!!hbsValue.length || disabled}
{readonly}
{id}
value={hbsValue.length ? `{{ ${hbsValue[0]} }}` : value}
value={(hbsValue.length ? `{{ ${hbsValue[0]} }}` : value) ?? ""}
placeholder={placeholder || ""}
on:click
on:blur

View File

@ -13,7 +13,7 @@
export let quiet = false
export let align: "left" | "right" | "center" | undefined = undefined
export let autofocus: boolean | null = false
export let autocomplete: boolean | undefined
export let autocomplete: boolean | string | undefined
const dispatch = createEventDispatcher()

View File

@ -1,26 +1,26 @@
<script>
<script lang="ts">
import Field from "./Field.svelte"
import EnvDropdown from "./Core/EnvDropdown.svelte"
import { createEventDispatcher } from "svelte"
import type { EnvDropdownType } from "../types"
export let value = null
export let label = null
export let labelPosition = "above"
export let placeholder = null
export let type = "text"
export let value: string | undefined = undefined
export let label: string | undefined = undefined
export let labelPosition: string = "above"
export let placeholder: string | undefined = undefined
export let type: EnvDropdownType = "text"
export let disabled = false
export let readonly = false
export let error = null
export let error: string | undefined = undefined
export let updateOnChange = true
export let quiet = false
export let autofocus
export let variables
export let showModal
export let helpText = null
export let environmentVariablesEnabled
export let handleUpgradePanel
export let autofocus: boolean = false
export let variables: { name: string }[] = []
export let showModal: () => void
export let helpText: string | undefined = undefined
export let environmentVariablesEnabled: boolean = false
export let handleUpgradePanel: () => void = () => {}
const dispatch = createEventDispatcher()
const onChange = e => {
const onChange = (e: any) => {
value = e.detail
dispatch("change", e.detail)
}
@ -29,13 +29,11 @@
<Field {helpText} {label} {labelPosition} {error}>
<EnvDropdown
{updateOnChange}
{error}
{disabled}
{readonly}
{value}
{placeholder}
{type}
{quiet}
{autofocus}
{variables}
{showModal}

View File

@ -14,7 +14,7 @@
export let updateOnChange = true
export let quiet = false
export let autofocus: boolean | undefined = undefined
export let autocomplete: boolean | undefined = undefined
export let autocomplete: boolean | string | undefined = undefined
export let helpText: string | undefined = undefined
const dispatch = createEventDispatcher()

View File

@ -3,7 +3,7 @@
import Icon from "../Icon/Icon.svelte"
const dispatch = createEventDispatcher()
const actionMenu = getContext("actionMenu") as { hideAll: () => void }
const actionMenu = getContext("actionMenu")
export let icon: string | undefined = undefined
export let disabled: boolean | undefined = undefined

View File

@ -1,7 +1,9 @@
import { ActionMenu } from "./types"
import { ModalContext } from "./types"
declare module "svelte" {
export function getContext(key: "actionMenu"): ActionMenu | undefined
export function getContext(key: "bbui-modal"): ModalContext
}
export const Modal = "bbui-modal"

View File

@ -111,3 +111,5 @@ export { banner, BANNER_TYPES } from "./Stores/banner"
// Helpers
export * as Helpers from "./helpers"
export type * from "./types"

View File

@ -1,3 +1,4 @@
export interface ActionMenu {
hide: () => void
hideAll: () => void
}

View File

@ -0,0 +1 @@
export type EnvDropdownType = "text" | "number" | "password" | "port"

View File

@ -0,0 +1,3 @@
export * from "./actionMenu"
export * from "./envDropdown"
export * from "./modalContext"

View File

@ -0,0 +1,3 @@
export interface ModalContext {
hide: () => void
}

View File

@ -1,4 +1,4 @@
<script>
<script lang="ts">
import ObjectField from "./fields/Object.svelte"
import BooleanField from "./fields/Boolean.svelte"
import LongFormField from "./fields/LongForm.svelte"
@ -6,15 +6,22 @@
import StringField from "./fields/String.svelte"
import SelectField from "./fields/Select.svelte"
export let type
export let value
export let error
export let name
export let config
export let showModal = () => {}
export let placeholder
type InputType =
| "string"
| "boolean"
| "object"
| "longForm"
| "fieldGroup"
| "select"
const selectComponent = type => {
export let type: InputType
export let value: any
export let error: string | null
export let name: string
export let config: any = undefined
export let placeholder: string | undefined = undefined
const selectComponent = (type: InputType) => {
if (type === "object") {
return ObjectField
} else if (type === "boolean") {
@ -40,7 +47,6 @@
{error}
{name}
{config}
{showModal}
{placeholder}
on:blur
on:change

View File

@ -1,33 +1,23 @@
<script>
import { Label, EnvDropdown } from "@budibase/bbui"
import { environment, licensing } from "@/stores/portal"
<script lang="ts">
import { Label } from "@budibase/bbui"
import EnvVariableInput from "@/components/portal/environment/EnvVariableInput.svelte"
export let type
export let name
export let value
export let error
export let placeholder
export let showModal = () => {}
async function handleUpgradePanel() {
await environment.upgradePanelOpened()
$licensing.goToUpgradePage()
}
</script>
<div class="form-row">
<Label>{name}</Label>
<EnvDropdown
<EnvVariableInput
on:change
on:blur
type={type === "port" ? "string" : type}
{value}
{error}
{placeholder}
variables={$environment.variables}
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
{showModal}
{handleUpgradePanel}
/>
</div>

View File

@ -1,19 +1,10 @@
<script>
import {
keepOpen,
Modal,
notifications,
Body,
Layout,
ModalContent,
} from "@budibase/bbui"
import { keepOpen, Body, Layout, ModalContent } from "@budibase/bbui"
import { processStringSync } from "@budibase/string-templates"
import CreateEditVariableModal from "@/components/portal/environment/CreateEditVariableModal.svelte"
import ConfigInput from "./ConfigInput.svelte"
import { createValidatedConfigStore } from "./stores/validatedConfig"
import { createValidatedNameStore } from "./stores/validatedName"
import { get } from "svelte/store"
import { environment } from "@/stores/portal"
export let integration
export let config
@ -39,24 +30,6 @@
return keepOpen
}
let createVariableModal
let configValueSetterCallback = () => {}
const showModal = setter => {
configValueSetterCallback = setter
createVariableModal.show()
}
async function saveVariable(data) {
try {
await environment.createVariable(data)
configValueSetterCallback(`{{ env.${data.name} }}`)
createVariableModal.hide()
} catch (err) {
notifications.error(`Failed to create variable: ${err.message}`)
}
}
</script>
<ModalContent
@ -79,7 +52,6 @@
value={$nameStore.name}
error={$nameStore.error}
name="Name"
showModal={() => showModal(nameStore.updateValue)}
on:blur={nameStore.markActive}
on:change={e => nameStore.updateValue(e.detail)}
/>
@ -94,15 +66,9 @@
{name}
{config}
{placeholder}
showModal={() =>
showModal(newValue => configStore.updateFieldValue(key, newValue))}
on:blur={() => configStore.markFieldActive(key)}
on:change={e => configStore.updateFieldValue(key, e.detail)}
/>
{/if}
{/each}
</ModalContent>
<Modal bind:this={createVariableModal}>
<CreateEditVariableModal save={saveVariable} />
</Modal>

View File

@ -9,10 +9,10 @@
export let value = ""
export let bindings = []
export let placeholder
export let placeholder = undefined
export let label
export let disabled = false
export let options
export let options = undefined
export let appendBindingsAsOptions = true
export let error

View File

@ -1,7 +1,7 @@
<script>
import { beforeUrlChange, goto, params } from "@roxi/routify"
import { datasources, flags, integrations, queries } from "@/stores/builder"
import { environment } from "@/stores/portal"
import {
Banner,
Body,
@ -407,13 +407,6 @@
notifications.error("Error getting datasources")
}
try {
// load the environment variables
await environment.loadVariables()
} catch (error) {
notifications.error(`Error getting environment variables - ${error}`)
}
datasource = $datasources.list.find(ds => ds._id === query?.datasourceId)
const datasourceUrl = datasource?.config.url
const qs = query?.fields.queryString

View File

@ -1,4 +1,4 @@
<script>
<script lang="ts">
import {
ModalContent,
Button,
@ -14,23 +14,23 @@
const modalContext = getContext(Context.Modal)
export let save
export let row
export let save: any
export let row: { name: string } | undefined = undefined
let deleteDialog
let deleteDialog: ConfirmDialog
let name = row?.name || ""
let productionValue
let developmentValue
let productionValue: string
let developmentValue: string
let useProductionValue = true
const HasSpacesRegex = /[\\"\s]/
const deleteVariable = async name => {
const deleteVariable = async (name: string) => {
try {
await environment.deleteVariable(name)
modalContext.hide()
notifications.success("Environment variable deleted")
} catch (err) {
} catch (err: any) {
notifications.error(err.message)
}
}
@ -43,7 +43,7 @@
development: developmentValue,
})
notifications.success("Environment variable saved")
} catch (err) {
} catch (err: any) {
notifications.error(`Error saving environment variable - ${err.message}`)
}
}
@ -55,10 +55,10 @@
title={!row ? "Add new environment variable" : "Edit environment variable"}
>
<Input
disabled={row}
disabled={!!row}
label="Name"
bind:value={name}
error={HasSpacesRegex.test(name) && "Must not include spaces"}
error={HasSpacesRegex.test(name) ? "Must not include spaces" : undefined}
/>
<div>
<Heading size="XS">Production</Heading>
@ -100,12 +100,12 @@
<ConfirmDialog
bind:this={deleteDialog}
onOk={() => {
deleteVariable(row.name)
deleteVariable(name)
}}
okText="Delete Environment Variable"
title="Confirm Deletion"
>
Are you sure you wish to delete the environment variable
<i>{row.name}?</i>
<i>{name}?</i>
This action cannot be undone.
</ConfirmDialog>

View File

@ -0,0 +1,58 @@
<script lang="ts">
import {
EnvDropdown,
Modal,
notifications,
type EnvDropdownType,
} from "@budibase/bbui"
import { environment, licensing } from "@/stores/portal"
import CreateEditVariableModal from "./CreateEditVariableModal.svelte"
import type { CreateEnvironmentVariableRequest } from "@budibase/types"
import { onMount } from "svelte"
export let label: string = ""
export let type: EnvDropdownType = "text"
export let value: string | undefined = undefined
export let error: string | undefined = undefined
export let placeholder: string | undefined = undefined
let modal: Modal
async function handleUpgradePanel() {
await environment.upgradePanelOpened()
$licensing.goToUpgradePage()
}
async function saveVariable(data: CreateEnvironmentVariableRequest) {
await environment.createVariable(data)
value = `{{ env.${data.name} }}`
modal.hide()
}
onMount(async () => {
try {
// load the environment variables
await environment.loadVariables()
} catch (error) {
notifications.error(`Error getting environment variables - ${error}`)
}
})
</script>
<EnvDropdown
on:change
on:blur
bind:value
{label}
type={type === "port" ? "text" : type}
{error}
{placeholder}
variables={$environment.variables}
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
showModal={() => modal.show()}
{handleUpgradePanel}
/>
<Modal bind:this={modal}>
<CreateEditVariableModal save={saveVariable} />
</Modal>

View File

@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { onMount } from "svelte"
import {
ModalContent,
@ -6,32 +6,52 @@
Select,
Body,
Input,
EnvDropdown,
Modal,
notifications,
} from "@budibase/bbui"
import { AUTH_TYPE_LABELS, AUTH_TYPES } from "./authTypes"
import { BindableCombobox } from "@/components/common/bindings"
import { getAuthBindings, getEnvironmentBindings } from "@/dataBinding"
import { environment, licensing, auth } from "@/stores/portal"
import CreateEditVariableModal from "@/components/portal/environment/CreateEditVariableModal.svelte"
import { environment, licensing } from "@/stores/portal"
import EnvVariableInput from "@/components/portal/environment/EnvVariableInput.svelte"
interface FormData {
name?: string
type?: string
basic: {
username?: string
password?: string
}
bearer: {
token?: string
}
}
export let configs
export let currentConfig
export let onConfirm
export let onRemove
let form = {
let form: FormData = {
basic: {},
bearer: {},
}
let errors = {
let errors: FormData = {
basic: {},
bearer: {},
}
let blurred = {
let blurred: {
name?: boolean
type?: boolean
basic: {
username?: boolean
password?: boolean
}
bearer: {
token?: boolean
}
} = {
basic: {},
bearer: {},
}
@ -39,19 +59,7 @@
let hasErrors = false
let hasChanged = false
let createVariableModal
let formFieldkey
onMount(async () => {
try {
await environment.loadVariables()
if ($auth.user) {
await licensing.init()
}
} catch (err) {
console.error(err)
}
if (currentConfig) {
deconstructConfig()
}
@ -79,7 +87,7 @@
* map the form into a new config to save by type
*/
const constructConfig = () => {
const newConfig = {
const newConfig: any = {
name: form.name,
type: form.type,
}
@ -123,10 +131,10 @@
errors.name =
// check for duplicate excluding the current config
configs.find(
c => c.name === form.name && c.name !== currentConfig?.name
(c: any) => c.name === form.name && c.name !== currentConfig?.name
) !== undefined
? "Name must be unique"
: null
: undefined
}
// Name required
else {
@ -137,17 +145,17 @@
// TYPE
const typeError = () => {
errors.type = form.type ? null : "Type is required"
errors.type = form.type ? undefined : "Type is required"
return !!errors.type
}
// BASIC AUTH
const basicAuthErrors = () => {
errors.basic.username = form.basic.username
? null
? undefined
: "Username is required"
errors.basic.password = form.basic.password
? null
? undefined
: "Password is required"
return !!(errors.basic.username || errors.basic.password || commonError)
@ -155,7 +163,7 @@
// BEARER TOKEN
const bearerTokenErrors = () => {
errors.bearer.token = form.bearer.token ? null : "Token is required"
errors.bearer.token = form.bearer.token ? undefined : "Token is required"
return !!(errors.bearer.token || commonError)
}
@ -169,16 +177,6 @@
}
}
const save = async data => {
try {
await environment.createVariable(data)
form.basic[formFieldkey] = `{{ env.${data.name} }}`
createVariableModal.hide()
} catch (err) {
notifications.error(`Failed to create variable: ${err.message}`)
}
}
const onFieldChange = () => {
checkErrors()
checkChanged()
@ -188,15 +186,13 @@
onConfirm(constructConfig())
}
async function handleUpgradePanel() {
await environment.upgradePanelOpened()
$licensing.goToUpgradePage()
}
function showModal(key) {
formFieldkey = key
createVariableModal.show()
}
onMount(async () => {
try {
await environment.loadVariables()
} catch (error) {
notifications.error(`Error getting environment variables - ${error}`)
}
})
</script>
<ModalContent
@ -221,7 +217,7 @@
bind:value={form.name}
on:change={onFieldChange}
on:blur={() => (blurred.name = true)}
error={blurred.name ? errors.name : null}
error={blurred.name ? errors.name : undefined}
/>
<Select
label="Type"
@ -229,31 +225,24 @@
on:change={onFieldChange}
options={AUTH_TYPE_LABELS}
on:blur={() => (blurred.type = true)}
error={blurred.type ? errors.type : null}
error={blurred.type ? errors.type : undefined}
/>
{#if form.type === AUTH_TYPES.BASIC}
<EnvDropdown
<EnvVariableInput
label="Username"
bind:value={form.basic.username}
on:change={onFieldChange}
on:blur={() => (blurred.basic.username = true)}
error={blurred.basic.username ? errors.basic.username : null}
showModal={() => showModal("configKey")}
variables={$environment.variables}
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
{handleUpgradePanel}
error={blurred.basic.username ? errors.basic.username : undefined}
/>
<EnvDropdown
<EnvVariableInput
label="Password"
type="password"
bind:value={form.basic.password}
on:change={onFieldChange}
on:blur={() => (blurred.basic.password = true)}
error={blurred.basic.password ? errors.basic.password : null}
showModal={() => showModal("configKey")}
variables={$environment.variables}
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
{handleUpgradePanel}
error={blurred.basic.password ? errors.basic.password : undefined}
/>
{/if}
{#if form.type === AUTH_TYPES.BEARER}
@ -274,16 +263,10 @@
blurred.bearer.token = true
onFieldChange()
}}
allowJS={false}
placeholder="Token"
appendBindingsAsOptions={true}
drawerEnabled={false}
error={blurred.bearer.token ? errors.bearer.token : null}
/>
{/if}
</Layout>
</ModalContent>
<Modal bind:this={createVariableModal}>
<CreateEditVariableModal {save} />
</Modal>

View File

@ -1,14 +1,15 @@
<script>
import { Heading, Layout } from "@budibase/bbui"
import { Heading, Layout, notifications } from "@budibase/bbui"
import KeyValueBuilder from "@/components/integration/KeyValueBuilder.svelte"
import ViewDynamicVariables from "./ViewDynamicVariables.svelte"
import { getEnvironmentBindings } from "@/dataBinding"
import { licensing } from "@/stores/portal"
import { environment, licensing } from "@/stores/portal"
import { queries } from "@/stores/builder"
import { cloneDeep } from "lodash/fp"
import SaveDatasourceButton from "../SaveDatasourceButton.svelte"
import Panel from "../Panel.svelte"
import Tooltip from "../Tooltip.svelte"
import { onMount } from "svelte"
export let datasource
@ -27,6 +28,14 @@
updatedDatasource.config.staticVariables = newStaticVariables
}
onMount(async () => {
try {
await environment.loadVariables()
} catch (error) {
notifications.error(`Error getting environment variables - ${error}`)
}
})
</script>
<Panel>

View File

@ -24,7 +24,6 @@
auth,
admin,
licensing,
environment,
enrichedApps,
sortBy,
} from "@/stores/portal"
@ -162,7 +161,6 @@
onMount(async () => {
try {
await environment.loadVariables()
// If the portal is loaded from an external URL with a template param
const initInfo = await auth.getInitInfo()
if (initInfo?.init_template) {