forked from extern/bruno
feat(#682): Enforce environment variable strictness
This commit is contained in:
parent
564825868e
commit
7267ba6485
@ -13,10 +13,12 @@ const Wrapper = styled.div`
|
|||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
|
|
||||||
&:nth-child(1),
|
&:nth-child(1),
|
||||||
&:nth-child(4),
|
&:nth-child(4) {
|
||||||
&:nth-child(5) {
|
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
|
&:nth-child(5) {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
&:nth-child(2) {
|
&:nth-child(2) {
|
||||||
width: 25%;
|
width: 25%;
|
||||||
|
@ -1,68 +1,79 @@
|
|||||||
import React, { useReducer } from 'react';
|
import React from 'react';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import { IconTrash } from '@tabler/icons';
|
import { IconTrash } from '@tabler/icons';
|
||||||
import { useTheme } from 'providers/Theme';
|
import { useTheme } from 'providers/Theme';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions';
|
import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions';
|
||||||
import reducer from './reducer';
|
|
||||||
import SingleLineEditor from 'components/SingleLineEditor';
|
import SingleLineEditor from 'components/SingleLineEditor';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { uuid } from 'utils/common';
|
||||||
|
|
||||||
const EnvironmentVariables = ({ environment, collection }) => {
|
const EnvironmentVariables = ({ environment, collection }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { storedTheme } = useTheme();
|
const { storedTheme } = useTheme();
|
||||||
const [state, reducerDispatch] = useReducer(reducer, { hasChanges: false, variables: environment.variables || [] });
|
|
||||||
const { variables, hasChanges } = state;
|
|
||||||
|
|
||||||
const saveChanges = () => {
|
const formik = useFormik({
|
||||||
dispatch(saveEnvironment(cloneDeep(variables), environment.uid, collection.uid))
|
enableReinitialize: true,
|
||||||
.then(() => {
|
initialValues: environment.variables || [],
|
||||||
toast.success('Changes saved successfully');
|
validationSchema: Yup.array().of(
|
||||||
reducerDispatch({
|
Yup.object({
|
||||||
type: 'CHANGES_SAVED'
|
enabled: Yup.boolean(),
|
||||||
});
|
name: Yup.string()
|
||||||
|
.required('Name cannot be empty')
|
||||||
|
.matches(/^[^\d]\w*$/, 'Name contains invalid characters')
|
||||||
|
.trim(),
|
||||||
|
secret: Yup.boolean(),
|
||||||
|
type: Yup.string(),
|
||||||
|
uid: Yup.string(),
|
||||||
|
value: Yup.string().trim()
|
||||||
})
|
})
|
||||||
.catch(() => toast.error('An error occurred while saving the changes'));
|
),
|
||||||
|
onSubmit: (values) => {
|
||||||
|
if (!formik.dirty) {
|
||||||
|
toast.error('Nothing to save');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(saveEnvironment(cloneDeep(values), environment.uid, collection.uid))
|
||||||
|
.then(() => {
|
||||||
|
toast.success('Changes saved successfully');
|
||||||
|
formik.resetForm({ values });
|
||||||
|
})
|
||||||
|
.catch(() => toast.error('An error occurred while saving the changes'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ErrorMessage = ({ name }) => {
|
||||||
|
const meta = formik.getFieldMeta(name);
|
||||||
|
console.log(name, meta);
|
||||||
|
if (!meta.error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<label htmlFor={name} className="text-red-500">
|
||||||
|
{meta.error}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addVariable = () => {
|
const addVariable = () => {
|
||||||
reducerDispatch({
|
const newVariable = {
|
||||||
type: 'ADD_VAR'
|
uid: uuid(),
|
||||||
});
|
name: '',
|
||||||
|
value: '',
|
||||||
|
type: 'text',
|
||||||
|
secret: false,
|
||||||
|
enabled: true
|
||||||
|
};
|
||||||
|
formik.setFieldValue(formik.values.length, newVariable, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleVarChange = (e, _variable, type) => {
|
const handleRemoveVar = (id) => {
|
||||||
const variable = cloneDeep(_variable);
|
formik.setValues(formik.values.filter((variable) => variable.uid !== id));
|
||||||
switch (type) {
|
|
||||||
case 'name': {
|
|
||||||
variable.name = e.target.value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'value': {
|
|
||||||
variable.value = e.target.value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'enabled': {
|
|
||||||
variable.enabled = e.target.checked;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'secret': {
|
|
||||||
variable.secret = e.target.checked;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reducerDispatch({
|
|
||||||
type: 'UPDATE_VAR',
|
|
||||||
variable
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveVars = (variable) => {
|
|
||||||
reducerDispatch({
|
|
||||||
type: 'DELETE_VAR',
|
|
||||||
variable
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -78,55 +89,57 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{variables && variables.length
|
{formik.values.map((variable, index) => (
|
||||||
? variables.map((variable, index) => {
|
<tr key={variable.uid}>
|
||||||
return (
|
<td className="text-center">
|
||||||
<tr key={variable.uid}>
|
<input
|
||||||
<td className="text-center">
|
type="checkbox"
|
||||||
<input
|
className="mr-3 mousetrap"
|
||||||
type="checkbox"
|
name={`${index}.enabled`}
|
||||||
checked={variable.enabled}
|
checked={variable.enabled}
|
||||||
className="mr-3 mousetrap"
|
onChange={formik.handleChange}
|
||||||
onChange={(e) => handleVarChange(e, variable, 'enabled')}
|
/>
|
||||||
/>
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<input
|
||||||
<input
|
type="text"
|
||||||
type="text"
|
autoComplete="off"
|
||||||
autoComplete="off"
|
autoCorrect="off"
|
||||||
autoCorrect="off"
|
autoCapitalize="off"
|
||||||
autoCapitalize="off"
|
spellCheck="false"
|
||||||
spellCheck="false"
|
className="mousetrap"
|
||||||
value={variable.name}
|
id={`${index}.name`}
|
||||||
className="mousetrap"
|
name={`${index}.name`}
|
||||||
onChange={(e) => handleVarChange(e, variable, 'name')}
|
value={formik.values[index].name}
|
||||||
/>
|
onChange={formik.handleChange}
|
||||||
</td>
|
/>
|
||||||
<td>
|
<ErrorMessage name={`${index}.name`} />
|
||||||
<SingleLineEditor
|
</td>
|
||||||
value={variable.value}
|
<td>
|
||||||
theme={storedTheme}
|
<SingleLineEditor
|
||||||
onChange={(newValue) => handleVarChange({ target: { value: newValue } }, variable, 'value')}
|
theme={storedTheme}
|
||||||
collection={collection}
|
collection={collection}
|
||||||
/>
|
name={`${index}.value`}
|
||||||
</td>
|
value={variable.value}
|
||||||
<td>
|
onChange={(newValue) => formik.setFieldValue(`${index}.value`, newValue, true)}
|
||||||
<input
|
/>
|
||||||
type="checkbox"
|
</td>
|
||||||
checked={variable.secret}
|
<td>
|
||||||
className="mr-3 mousetrap"
|
<input
|
||||||
onChange={(e) => handleVarChange(e, variable, 'secret')}
|
type="checkbox"
|
||||||
/>
|
className="mr-3 mousetrap"
|
||||||
</td>
|
name={`${index}.secret`}
|
||||||
<td>
|
checked={variable.secret}
|
||||||
<button onClick={() => handleRemoveVars(variable)}>
|
onChange={formik.handleChange}
|
||||||
<IconTrash strokeWidth={1.5} size={20} />
|
/>
|
||||||
</button>
|
</td>
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
<button onClick={() => handleRemoveVar(variable.uid)}>
|
||||||
);
|
<IconTrash strokeWidth={1.5} size={20} />
|
||||||
})
|
</button>
|
||||||
: null}
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@ -137,12 +150,7 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button type="submit" className="submit btn btn-md btn-secondary mt-2" onClick={formik.handleSubmit}>
|
||||||
type="submit"
|
|
||||||
className="submit btn btn-md btn-secondary mt-2"
|
|
||||||
disabled={!hasChanges}
|
|
||||||
onClick={saveChanges}
|
|
||||||
>
|
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
import produce from 'immer';
|
|
||||||
import find from 'lodash/find';
|
|
||||||
import filter from 'lodash/filter';
|
|
||||||
import { uuid } from 'utils/common';
|
|
||||||
|
|
||||||
const reducer = (state, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'ADD_VAR': {
|
|
||||||
return produce(state, (draft) => {
|
|
||||||
draft.variables.push({
|
|
||||||
uid: uuid(),
|
|
||||||
name: '',
|
|
||||||
value: '',
|
|
||||||
type: 'text',
|
|
||||||
secret: false,
|
|
||||||
enabled: true
|
|
||||||
});
|
|
||||||
draft.hasChanges = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'UPDATE_VAR': {
|
|
||||||
return produce(state, (draft) => {
|
|
||||||
const variable = find(draft.variables, (v) => v.uid === action.variable.uid);
|
|
||||||
variable.name = action.variable.name;
|
|
||||||
variable.value = action.variable.value;
|
|
||||||
variable.enabled = action.variable.enabled;
|
|
||||||
variable.secret = action.variable.secret;
|
|
||||||
draft.hasChanges = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'DELETE_VAR': {
|
|
||||||
return produce(state, (draft) => {
|
|
||||||
draft.variables = filter(draft.variables, (v) => v.uid !== action.variable.uid);
|
|
||||||
draft.hasChanges = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'CHANGES_SAVED': {
|
|
||||||
return produce(state, (draft) => {
|
|
||||||
draft.hasChanges = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default reducer;
|
|
Loading…
Reference in New Issue
Block a user