diff --git a/models/configuration_k_v.go b/models/configuration_k_v.go index c6421982c..68c296a35 100644 --- a/models/configuration_k_v.go +++ b/models/configuration_k_v.go @@ -25,6 +25,7 @@ package models import ( "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) @@ -34,6 +35,9 @@ import ( // swagger:model configurationKV type ConfigurationKV struct { + // env override + EnvOverride *EnvOverride `json:"env_override,omitempty"` + // key Key string `json:"key,omitempty"` @@ -43,11 +47,64 @@ type ConfigurationKV struct { // Validate validates this configuration k v func (m *ConfigurationKV) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateEnvOverride(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } return nil } -// ContextValidate validates this configuration k v based on context it is used +func (m *ConfigurationKV) validateEnvOverride(formats strfmt.Registry) error { + if swag.IsZero(m.EnvOverride) { // not required + return nil + } + + if m.EnvOverride != nil { + if err := m.EnvOverride.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("env_override") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("env_override") + } + return err + } + } + + return nil +} + +// ContextValidate validate this configuration k v based on the context it is used func (m *ConfigurationKV) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateEnvOverride(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ConfigurationKV) contextValidateEnvOverride(ctx context.Context, formats strfmt.Registry) error { + + if m.EnvOverride != nil { + if err := m.EnvOverride.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("env_override") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("env_override") + } + return err + } + } + return nil } diff --git a/models/env_override.go b/models/env_override.go new file mode 100644 index 000000000..79cb2903c --- /dev/null +++ b/models/env_override.go @@ -0,0 +1,70 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// EnvOverride env override +// +// swagger:model envOverride +type EnvOverride struct { + + // name + Name string `json:"name,omitempty"` + + // value + Value string `json:"value,omitempty"` +} + +// Validate validates this env override +func (m *EnvOverride) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this env override based on context it is used +func (m *EnvOverride) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *EnvOverride) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *EnvOverride) UnmarshalBinary(b []byte) error { + var res EnvOverride + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/portal-ui/src/screens/Console/Configurations/types.ts b/portal-ui/src/screens/Console/Configurations/types.ts index 3abc113b4..0e89df450 100644 --- a/portal-ui/src/screens/Console/Configurations/types.ts +++ b/portal-ui/src/screens/Console/Configurations/types.ts @@ -49,9 +49,15 @@ export interface IConfigurationElement { url?: string; } +export interface IEnvOverride { + name: string; + value: string; +} + export interface IElementValue { key: string; value: string; + env_override?: IEnvOverride; } export interface IConfigurationSys { @@ -65,3 +71,12 @@ export interface IElement { icon?: any; disabled?: boolean; } + +export interface OverrideValue { + value: string; + overrideEnv: string; +} + +export interface IOverrideEnv { + [key: string]: OverrideValue; +} diff --git a/portal-ui/src/screens/Console/Configurations/utils.tsx b/portal-ui/src/screens/Console/Configurations/utils.tsx index 5a0575e4c..7cecb77d4 100644 --- a/portal-ui/src/screens/Console/Configurations/utils.tsx +++ b/portal-ui/src/screens/Console/Configurations/utils.tsx @@ -22,7 +22,7 @@ import FindReplaceIcon from "@mui/icons-material/FindReplace"; import VpnKeyIcon from "@mui/icons-material/VpnKey"; import PendingActionsIcon from "@mui/icons-material/PendingActions"; import CallToActionIcon from "@mui/icons-material/CallToAction"; -import { IElement, IElementValue } from "./types"; +import { IElement, IElementValue, IOverrideEnv, OverrideValue } from "./types"; export const configurationElements: IElement[] = [ { @@ -309,3 +309,21 @@ export const selectSAs = ( setSelectedSAs(elements); return elements; }; + +export const overrideFields = (formFields: IElementValue[]): IOverrideEnv => { + let overrideReturn: IOverrideEnv = {}; + + formFields.forEach((envItem) => { + // it has override values, we construct the value + if (envItem.env_override) { + const value: OverrideValue = { + value: envItem.env_override.value, + overrideEnv: envItem.env_override.name, + }; + + overrideReturn = { ...overrideReturn, [envItem.key]: value }; + } + }); + + return overrideReturn; +}; diff --git a/portal-ui/src/screens/Console/EventDestinations/ConfTargetGeneric.tsx b/portal-ui/src/screens/Console/EventDestinations/ConfTargetGeneric.tsx index 98b50b773..202379054 100644 --- a/portal-ui/src/screens/Console/EventDestinations/ConfTargetGeneric.tsx +++ b/portal-ui/src/screens/Console/EventDestinations/ConfTargetGeneric.tsx @@ -19,7 +19,7 @@ import { Theme } from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; import Grid from "@mui/material/Grid"; -import { IElementValue, KVField } from "../Configurations/types"; +import { IElementValue, IOverrideEnv, KVField } from "../Configurations/types"; import { formFieldStyles, modalBasic, @@ -28,11 +28,14 @@ import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWr import CSVMultiSelector from "../Common/FormComponents/CSVMultiSelector/CSVMultiSelector"; import CommentBoxWrapper from "../Common/FormComponents/CommentBoxWrapper/CommentBoxWrapper"; import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; +import PredefinedList from "../Common/FormComponents/PredefinedList/PredefinedList"; +import { ConsoleIcon, Tooltip } from "mds"; interface IConfGenericProps { onChange: (newValue: IElementValue[]) => void; fields: KVField[]; defaultVals?: IElementValue[]; + overrideEnv?: IOverrideEnv; classes: any; } @@ -69,6 +72,7 @@ const ConfTargetGeneric = ({ onChange, fields, defaultVals, + overrideEnv, classes, }: IConfGenericProps) => { const [valueHolder, setValueHolder] = useState([]); @@ -102,9 +106,42 @@ const ConfTargetGeneric = ({ }; const fieldDefinition = (field: KVField, item: number) => { + const holderItem = valueHolder[item]; + + if (holderItem) { + // Override Value with env var, we display generic string component + const override = overrideEnv?.[`${holderItem.key}`]; + + if (override) { + return ( + + + + + + } + /> + ); + } + } + switch (field.type) { case "on|off": - const value = valueHolder[item] ? valueHolder[item].value : "off"; + const value = holderItem ? holderItem.value : "off"; return ( { @@ -141,7 +178,7 @@ const ConfTargetGeneric = ({ name={field.name} label={field.label} tooltip={field.tooltip} - value={valueHolder[item] ? valueHolder[item].value : ""} + value={holderItem ? holderItem.value : ""} onChange={(e: React.ChangeEvent) => setValueElement(field.name, e.target.value, item) } @@ -155,7 +192,7 @@ const ConfTargetGeneric = ({ name={field.name} label={field.label} tooltip={field.tooltip} - value={valueHolder[item] ? valueHolder[item].value : ""} + value={holderItem ? holderItem.value : ""} onChange={(e: React.ChangeEvent) => setValueElement(field.name, e.target.value, item) } diff --git a/portal-ui/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.tsx b/portal-ui/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.tsx index acee4aaa6..4ca81c430 100644 --- a/portal-ui/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.tsx +++ b/portal-ui/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.tsx @@ -32,12 +32,14 @@ import { } from "../../Common/FormComponents/common/styleLibrary"; import { fieldsConfigurations, + overrideFields, removeEmptyFields, } from "../../Configurations/utils"; import { IConfigurationElement, IConfigurationSys, IElementValue, + IOverrideEnv, } from "../../Configurations/types"; import { ErrorResponseHandler } from "../../../../common/types"; import ResetConfigurationModal from "./ResetConfigurationModal"; @@ -89,6 +91,7 @@ const EditConfiguration = ({ ); const [resetConfigurationOpen, setResetConfigurationOpen] = useState(false); + const [overrideEnvs, setOverrideEnvs] = useState({}); const loadingConfig = useSelector( (state: AppState) => state.system.loadingConfigurations @@ -109,6 +112,7 @@ const EditConfiguration = ({ setConfigSubsysList(res); const keyVals = get(res[0], "key_values", []); setConfigValues(keyVals); + setOverrideEnvs(overrideFields(keyVals)); dispatch(configurationIsLoading(false)); }) .catch((err: ErrorResponseHandler) => { @@ -221,6 +225,7 @@ const EditConfiguration = ({ } onChange={onValueChange} defaultVals={configValues} + overrideEnv={overrideEnvs} /> . import React, { Fragment, useEffect, useState } from "react"; -import { Button, Grid } from "mds"; +import { Button, ConsoleIcon, Grid, Tooltip } from "mds"; import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; import { Webhook } from "@mui/icons-material"; @@ -34,6 +34,8 @@ import { useAppDispatch } from "../../../../store"; import { LinearProgress } from "@mui/material"; import { IConfigurationSys } from "../../Configurations/types"; import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; +import PredefinedList from "../../Common/FormComponents/PredefinedList/PredefinedList"; +import { overrideFields } from "../../Configurations/utils"; interface IEndpointModal { open: boolean; @@ -166,6 +168,11 @@ const EditEndpointModal = ({ }; const defaultWH = !name.includes(":"); + const hasOverride = endpointInfo.key_values.filter( + (itm) => !!itm.env_override + ); + + const overrideValues = overrideFields(hasOverride); let title = "Edit Webhook"; let icon = ; @@ -181,6 +188,10 @@ const EditEndpointModal = ({ break; } + if (hasOverride.length > 0) { + title = "View env variable Webhook"; + } + return ( - - ) => { - const value = e.target.checked ? "on" : "off"; - setEndpointState(value); - }} - id={"endpoint_enabled"} - name={"endpoint_enabled"} - label={"Enabled"} - value={"switch_on"} - checked={endpointState === "on"} - /> - - - ) => { - setEndpoint(event.target.value); - validateInput("endpoint", event.target.validity.valid); - }} - error={ - invalidInputs.includes("endpoint") ? "Invalid Endpoint set" : "" - } - label="Endpoint" - value={endpoint} - pattern={ - "^(https?):\\/\\/([a-zA-Z0-9\\-.]+)(:[0-9]+)?(\\/[a-zA-Z0-9\\-.\\/]*)?$" - } - required - /> - - - ) => { - setAuthToken(event.target.value); - }} - label="Auth Token" - value={authToken} - /> - - {saving && ( - - - + {hasOverride.length > 0 ? ( + + + + + + + + } + /> + + + + + + + + } + /> + + + + + + + + } + /> + + + ) : ( + + + ) => { + const value = e.target.checked ? "on" : "off"; + setEndpointState(value); + }} + id={"endpoint_enabled"} + name={"endpoint_enabled"} + label={"Enabled"} + value={"switch_on"} + checked={endpointState === "on"} + /> + + + ) => { + setEndpoint(event.target.value); + validateInput("endpoint", event.target.validity.valid); + }} + error={ + invalidInputs.includes("endpoint") + ? "Invalid Endpoint set" + : "" + } + label="Endpoint" + value={endpoint} + pattern={ + "^(https?):\\/\\/([a-zA-Z0-9\\-.]+)(:[0-9]+)?(\\/[a-zA-Z0-9\\-.\\/]*)?$" + } + required + /> + + + ) => { + setAuthToken(event.target.value); + }} + label="Auth Token" + value={authToken} + /> + + {saving && ( + + + + )} + +