From 15bd0fbc846f5764c93a9505f4bd5c685a02d14a Mon Sep 17 00:00:00 2001
From: Youssef Khedher
Date: Mon, 7 Mar 2022 13:44:46 +0100
Subject: [PATCH] feat: validate json (#764)
* feat: add isJSON function
* feat: validate JSON input
* feat: add JSON code editor
* feat: add error message for JSON payload
* feat: validate JSON input
* fix: merge conflict
* fix: conflict in AddFeatureVariant
* refactor: remove code editor for JSON input
* fix: update PR based on feedback
* fix: revert yarn.lock
* fix: revert yarn.lock
* fix: update PR based on feedback
* fix: styles
* fix: json input error message
* fix: remove ts-expect-error
* refactor: change inputProps type
* fix: import InputProps
---
frontend/src/component/common/Input/Input.tsx | 4 +-
.../AddFeatureVariant.styles.ts | 29 +++++
.../AddFeatureVariant/AddFeatureVariant.tsx | 116 +++++++++---------
.../FeatureVariantsList.tsx | 2 +-
4 files changed, 88 insertions(+), 63 deletions(-)
create mode 100644 frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.styles.ts
diff --git a/frontend/src/component/common/Input/Input.tsx b/frontend/src/component/common/Input/Input.tsx
index 7afcaed430..546896a924 100644
--- a/frontend/src/component/common/Input/Input.tsx
+++ b/frontend/src/component/common/Input/Input.tsx
@@ -1,4 +1,4 @@
-import { InputLabelProps, TextField } from '@material-ui/core';
+import { InputLabelProps, InputProps, TextField } from '@material-ui/core';
import { INPUT_ERROR_TEXT } from '../../../testIds';
import { useStyles } from './Input.styles';
import React from 'react';
@@ -15,6 +15,7 @@ interface IInputProps extends React.InputHTMLAttributes {
onBlur?: (e: any) => any;
multiline?: boolean;
rows?: number;
+ InputProps?: Partial;
InputLabelProps?: Partial;
}
@@ -27,6 +28,7 @@ const Input = ({
className,
value,
onChange,
+ InputProps,
...rest
}: IInputProps) => {
const styles = useStyles();
diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.styles.ts b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.styles.ts
new file mode 100644
index 0000000000..0aba375799
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.styles.ts
@@ -0,0 +1,29 @@
+import { makeStyles } from '@material-ui/core/styles';
+
+export const useStyles = makeStyles(theme => ({
+ error: {
+ color: theme.palette.error.main,
+ fontSize: theme.fontSizes.smallBody,
+ position: 'relative',
+ },
+ input: {
+ maxWidth: 350,
+ width: '100%',
+ },
+ grid: {
+ marginBottom: '0.5rem',
+ },
+ weightInput: {
+ marginRight: '0.8rem',
+ },
+ label: { marginBottom: '1rem' },
+ info: {
+ width: '18.5px',
+ height: '18.5px',
+ color: 'grey',
+ },
+ select: {
+ minWidth: '100px',
+ width: '100%',
+ },
+}));
diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.tsx b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.tsx
index a8eb0ad990..b97fb5832b 100644
--- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.tsx
@@ -5,24 +5,26 @@ import {
FormControlLabel,
Grid,
InputAdornment,
- TextField,
Tooltip,
} from '@material-ui/core';
import { Info } from '@material-ui/icons';
import { weightTypes } from './enums';
import { OverrideConfig } from './OverrideConfig/OverrideConfig';
-import ConditionallyRender from '../../../../../common/ConditionallyRender';
+import ConditionallyRender from 'component/common/ConditionallyRender';
import { useCommonStyles } from 'common.styles';
-import Dialogue from '../../../../../common/Dialogue';
+import Dialogue from 'component/common/Dialogue';
import { modalStyles, trim } from 'component/common/util';
-import PermissionSwitch from '../../../../../common/PermissionSwitch/PermissionSwitch';
+import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
import { UPDATE_FEATURE_VARIANTS } from 'component/providers/AccessProvider/permissions';
-import useFeature from '../../../../../../hooks/api/getters/useFeature/useFeature';
+import useFeature from 'hooks/api/getters/useFeature/useFeature';
import { useParams } from 'react-router-dom';
import { IFeatureViewParams } from 'interfaces/params';
import { IFeatureVariant, IOverride } from 'interfaces/featureToggle';
import cloneDeep from 'lodash.clonedeep';
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
+import { useStyles } from './AddFeatureVariant.styles';
+import Input from 'component/common/Input/Input';
+import { formatUnknownError } from 'utils/format-unknown-error';
const payloadOptions = [
{ key: 'string', label: 'string' },
@@ -43,7 +45,7 @@ interface IAddVariantProps {
editing: boolean;
}
-const AddVariant = ({
+export const AddVariant = ({
showDialog,
closeDialog,
save,
@@ -53,6 +55,7 @@ const AddVariant = ({
title,
editing,
}: IAddVariantProps) => {
+ const styles = useStyles();
const [data, setData] = useState>({});
const [payload, setPayload] = useState(EMPTY_PAYLOAD);
const [overrides, setOverrides] = useState([]);
@@ -62,6 +65,18 @@ const AddVariant = ({
const { feature } = useFeature(projectId, featureId);
const [variants, setVariants] = useState([]);
+ const isValidJSON = (input: string): boolean => {
+ try {
+ JSON.parse(input);
+ return true;
+ } catch (e: unknown) {
+ setError({
+ payload: 'Invalid JSON',
+ });
+ return false;
+ }
+ };
+
const clear = () => {
if (editVariant) {
setData({
@@ -136,6 +151,11 @@ const AddVariant = ({
setError(weightValidation);
return;
}
+ const validJSON =
+ payload.type === 'json' && !isValidJSON(payload.value);
+ if (validJSON) {
+ return;
+ }
try {
const variant: IFeatureVariant = {
@@ -154,19 +174,14 @@ const AddVariant = ({
await save(variant);
clear();
closeDialog();
- } catch (error) {
- // @ts-expect-error
- if (error?.body?.details[0]?.message?.includes('duplicate value')) {
+ } catch (e: unknown) {
+ const error = formatUnknownError(e);
+ if (error.includes('duplicate value')) {
setError({ name: 'A variant with that name already exists.' });
- } else if (
- // @ts-expect-error
- error?.body?.details[0]?.message?.includes('must be a number')
- ) {
+ } else if (error.includes('must be a number')) {
setError({ weight: 'Weight must be a number' });
} else {
- const msg =
- // @ts-expect-error
- error?.body?.details[0]?.message || 'Could not add variant';
+ const msg = error || 'Could not add variant';
setError({ general: msg });
}
}
@@ -174,6 +189,7 @@ const AddVariant = ({
const onPayload = (e: ChangeEvent<{ name?: string; value: unknown }>) => {
e.preventDefault();
+ setError({ payload: '' });
setPayload({
...payload,
// @ts-expect-error
@@ -248,20 +264,16 @@ const AddVariant = ({
onSubmit={submit}
className={commonStyles.contentSpacingY}
>
- {error.general}
- {error.general}
+ 0)
}
show={
-
+
-
),
}}
- style={{ marginRight: '0.8rem' }}
+ className={styles.weightInput}
value={data.weight}
error={Boolean(error.weight)}
- helperText={error.weight}
+ errorText={error.weight}
type="number"
disabled={!isFixWeight}
onChange={e => {
@@ -339,19 +344,13 @@ const AddVariant = ({
}
/>
-
+
Payload
-
+
@@ -360,40 +359,37 @@ const AddVariant = ({
id="variant-payload-type"
name="type"
label="Type"
- className={commonStyles.fullWidth}
+ className={styles.select}
value={payload.type}
options={payloadOptions}
onChange={onPayload}
- style={{ minWidth: '100px', width: '100%' }}
/>
-
0}
show={
-
+
Overrides
-
+
}
@@ -410,10 +406,8 @@ const AddVariant = ({
color="primary"
>
Add override
- {' '}
+
);
};
-
-export default AddVariant;
diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/FeatureVariantsList.tsx b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/FeatureVariantsList.tsx
index 129624d4f4..56a6c44acc 100644
--- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/FeatureVariantsList.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/FeatureVariantsList.tsx
@@ -10,7 +10,7 @@ import {
TableRow,
Typography,
} from '@material-ui/core';
-import AddVariant from './AddFeatureVariant/AddFeatureVariant';
+import { AddVariant } from './AddFeatureVariant/AddFeatureVariant';
import { useContext, useEffect, useState } from 'react';
import useFeature from '../../../../../hooks/api/getters/useFeature/useFeature';