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;
|
||||
|
||||
&:nth-child(1),
|
||||
&:nth-child(4),
|
||||
&:nth-child(5) {
|
||||
&:nth-child(4) {
|
||||
width: 70px;
|
||||
}
|
||||
&:nth-child(5) {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 25%;
|
||||
|
@ -1,68 +1,79 @@
|
||||
import React, { useReducer } from 'react';
|
||||
import React from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { IconTrash } from '@tabler/icons';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import reducer from './reducer';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import { uuid } from 'utils/common';
|
||||
|
||||
const EnvironmentVariables = ({ environment, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
const [state, reducerDispatch] = useReducer(reducer, { hasChanges: false, variables: environment.variables || [] });
|
||||
const { variables, hasChanges } = state;
|
||||
|
||||
const saveChanges = () => {
|
||||
dispatch(saveEnvironment(cloneDeep(variables), environment.uid, collection.uid))
|
||||
.then(() => {
|
||||
toast.success('Changes saved successfully');
|
||||
reducerDispatch({
|
||||
type: 'CHANGES_SAVED'
|
||||
});
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: environment.variables || [],
|
||||
validationSchema: Yup.array().of(
|
||||
Yup.object({
|
||||
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 = () => {
|
||||
reducerDispatch({
|
||||
type: 'ADD_VAR'
|
||||
});
|
||||
const newVariable = {
|
||||
uid: uuid(),
|
||||
name: '',
|
||||
value: '',
|
||||
type: 'text',
|
||||
secret: false,
|
||||
enabled: true
|
||||
};
|
||||
formik.setFieldValue(formik.values.length, newVariable, false);
|
||||
};
|
||||
|
||||
const handleVarChange = (e, _variable, type) => {
|
||||
const variable = cloneDeep(_variable);
|
||||
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
|
||||
});
|
||||
const handleRemoveVar = (id) => {
|
||||
formik.setValues(formik.values.filter((variable) => variable.uid !== id));
|
||||
};
|
||||
|
||||
return (
|
||||
@ -78,55 +89,57 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{variables && variables.length
|
||||
? variables.map((variable, index) => {
|
||||
return (
|
||||
<tr key={variable.uid}>
|
||||
<td className="text-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={variable.enabled}
|
||||
className="mr-3 mousetrap"
|
||||
onChange={(e) => handleVarChange(e, variable, 'enabled')}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={variable.name}
|
||||
className="mousetrap"
|
||||
onChange={(e) => handleVarChange(e, variable, 'name')}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<SingleLineEditor
|
||||
value={variable.value}
|
||||
theme={storedTheme}
|
||||
onChange={(newValue) => handleVarChange({ target: { value: newValue } }, variable, 'value')}
|
||||
collection={collection}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={variable.secret}
|
||||
className="mr-3 mousetrap"
|
||||
onChange={(e) => handleVarChange(e, variable, 'secret')}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button onClick={() => handleRemoveVars(variable)}>
|
||||
<IconTrash strokeWidth={1.5} size={20} />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
{formik.values.map((variable, index) => (
|
||||
<tr key={variable.uid}>
|
||||
<td className="text-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mr-3 mousetrap"
|
||||
name={`${index}.enabled`}
|
||||
checked={variable.enabled}
|
||||
onChange={formik.handleChange}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
className="mousetrap"
|
||||
id={`${index}.name`}
|
||||
name={`${index}.name`}
|
||||
value={formik.values[index].name}
|
||||
onChange={formik.handleChange}
|
||||
/>
|
||||
<ErrorMessage name={`${index}.name`} />
|
||||
</td>
|
||||
<td>
|
||||
<SingleLineEditor
|
||||
theme={storedTheme}
|
||||
collection={collection}
|
||||
name={`${index}.value`}
|
||||
value={variable.value}
|
||||
onChange={(newValue) => formik.setFieldValue(`${index}.value`, newValue, true)}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mr-3 mousetrap"
|
||||
name={`${index}.secret`}
|
||||
checked={variable.secret}
|
||||
onChange={formik.handleChange}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button onClick={() => handleRemoveVar(variable.uid)}>
|
||||
<IconTrash strokeWidth={1.5} size={20} />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@ -137,12 +150,7 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
className="submit btn btn-md btn-secondary mt-2"
|
||||
disabled={!hasChanges}
|
||||
onClick={saveChanges}
|
||||
>
|
||||
<button type="submit" className="submit btn btn-md btn-secondary mt-2" onClick={formik.handleSubmit}>
|
||||
Save
|
||||
</button>
|
||||
</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