Repository: provectus/kafka-ui
Issue: SR: Display a warning in case of invalid syntax
Labels: type/enhancement
good first issue
scope/frontend
status/accepted


**Describe the bug**
It's possible to submit data filled not in {} in case of Schemas editing

**Set up**
https://www.kafka-ui.provectus.io/


**Steps to Reproduce**
<!-- We'd like you to provide an example setup (via docker-compose, helm, etc.) 
to reproduce the problem, especially with a complex setups. -->
Steps to reproduce the behavior:

1. Navigate to Schema Registry
2. Select the Schema
3. Edit the Schema 
4. Add the data after {}
5. Press "Submit"


**Expected behavior**
Warning should appear to say that invalid data was filled or should not be possible to submit if data added not in {} scope

**Screenshots**
<img width="1726" alt="invalid data within json" src="https://user-images.githubusercontent.com/104780608/181295973-3e1a4e01-cb7e-4e54-9810-22a577479758.png">






Issue comments:
Considering #2354 it seems like we have validation bound to producing messages. There should be one for creating/updating schemas as well.

Pull request review comments:
I don't think we need this `eslint-disable` here
@Haarolean pls help us with error message
```suggestion
    'Schema syntax is not valid',
```
@habrahamyanpro is it a generic message for all the errors? If we'd stick with "invalid data" message, that's gonna be ambiguous. 
Pls check this test again and fix 
same
```suggestion
  config: yup.string().required().isValidJsonObject(),
```
i think this check `e`  could make invalid values true like `eWrong = {}`
`
trimmedValue.indexOf('enum') === 0`
i replace 'e'  with 'enum', also added test cases 

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/components/Schemas/Edit/Form.tsx b/kafka-ui-react-app/src/components/Schemas/Edit/Form.tsx
index 9ce7f280f43..2fce1ad7d79 100644
--- a/kafka-ui-react-app/src/components/Schemas/Edit/Form.tsx
+++ b/kafka-ui-react-app/src/components/Schemas/Edit/Form.tsx
@@ -10,6 +10,7 @@ import {
   clusterSchemasPath,
   ClusterSubjectParam,
 } from 'lib/paths';
+import yup from 'lib/yupExtended';
 import { NewSchemaSubjectRaw } from 'redux/interfaces';
 import Editor from 'components/common/Editor/Editor';
 import Select from 'components/common/Select/Select';
@@ -28,6 +29,9 @@ import {
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import { schemasApiClient } from 'lib/api';
 import { showServerError } from 'lib/errorHandling';
+import { yupResolver } from '@hookform/resolvers/yup';
+import { FormError } from 'components/common/Input/Input.styled';
+import { ErrorMessage } from '@hookform/error-message';
 
 import * as S from './Edit.styled';
 
@@ -47,8 +51,16 @@ const Form: React.FC = () => {
       : JSON.stringify(JSON.parse(schema?.schema || '{}'), null, '\t');
   }, [schema]);
 
+  const validationSchema = () =>
+    yup.object().shape({
+      newSchema:
+        schema?.schemaType === SchemaType.PROTOBUF
+          ? yup.string().required().isEnum('Schema syntax is not valid')
+          : yup.string().required().isJsonObject('Schema syntax is not valid'),
+    });
   const methods = useForm<NewSchemaSubjectRaw>({
     mode: 'onChange',
+    resolver: yupResolver(validationSchema()),
     defaultValues: {
       schemaType: schema?.schemaType,
       compatibilityLevel:
@@ -58,11 +70,10 @@ const Form: React.FC = () => {
   });
 
   const {
-    formState: { isDirty, isSubmitting, dirtyFields },
+    formState: { isDirty, isSubmitting, dirtyFields, errors },
     control,
     handleSubmit,
   } = methods;
-
   const onSubmit = async (props: NewSchemaSubjectRaw) => {
     if (!schema) return;
 
@@ -191,11 +202,14 @@ const Form: React.FC = () => {
                   )}
                 />
               </S.EditorContainer>
+              <FormError>
+                <ErrorMessage errors={errors} name="newSchema" />
+              </FormError>
               <Button
                 buttonType="primary"
                 buttonSize="M"
                 type="submit"
-                disabled={!isDirty || isSubmitting}
+                disabled={!isDirty || isSubmitting || !!errors.newSchema}
               >
                 Submit
               </Button>
diff --git a/kafka-ui-react-app/src/lib/__test__/yupExtended.spec.ts b/kafka-ui-react-app/src/lib/__test__/yupExtended.spec.ts
index bd43dd3f72a..8100b9a3264 100644
--- a/kafka-ui-react-app/src/lib/__test__/yupExtended.spec.ts
+++ b/kafka-ui-react-app/src/lib/__test__/yupExtended.spec.ts
@@ -1,5 +1,19 @@
-import { isValidJsonObject } from 'lib/yupExtended';
+import { isValidEnum, isValidJsonObject } from 'lib/yupExtended';
 
+const invalidEnum = `
+ennum SchemType {
+  AVRO = 0;
+  JSON = 1;
+  PROTOBUF = 3;
+}
+`;
+const validEnum = `
+enum SchemType {
+  AVRO = 0;
+  JSON = 1;
+  PROTOBUF = 3;
+}
+`;
 describe('yup extended', () => {
   describe('isValidJsonObject', () => {
     it('returns false for no value', () => {
@@ -21,4 +35,21 @@ describe('yup extended', () => {
       expect(isValidJsonObject('{ "foo": "bar" }')).toBeTruthy();
     });
   });
+
+  describe('isValidEnum', () => {
+    it('returns false for invalid enum', () => {
+      expect(isValidEnum(invalidEnum)).toBeFalsy();
+    });
+    it('returns false for no value', () => {
+      expect(isValidEnum()).toBeFalsy();
+    });
+    it('returns true should trim value', () => {
+      expect(
+        isValidEnum(`  enum SchemType {AVRO = 0; PROTOBUF = 3;}   `)
+      ).toBeTruthy();
+    });
+    it('returns true for valid enum', () => {
+      expect(isValidEnum(validEnum)).toBeTruthy();
+    });
+  });
 });
diff --git a/kafka-ui-react-app/src/lib/yupExtended.ts b/kafka-ui-react-app/src/lib/yupExtended.ts
index 9c96e073db5..4c662ca8222 100644
--- a/kafka-ui-react-app/src/lib/yupExtended.ts
+++ b/kafka-ui-react-app/src/lib/yupExtended.ts
@@ -9,7 +9,8 @@ declare module 'yup' {
     TDefault = undefined,
     TFlags extends yup.Flags = ''
   > extends yup.Schema<TType, TContext, TDefault, TFlags> {
-    isJsonObject(): StringSchema<TType, TContext>;
+    isJsonObject(message?: string): StringSchema<TType, TContext>;
+    isEnum(message?: string): StringSchema<TType, TContext>;
   }
 }
 
@@ -31,15 +32,40 @@ export const isValidJsonObject = (value?: string) => {
   return false;
 };
 
-const isJsonObject = () => {
+const isJsonObject = (message?: string) => {
   return yup.string().test(
     'isJsonObject',
     // eslint-disable-next-line no-template-curly-in-string
-    '${path} is not JSON object',
+    message || '${path} is not JSON object',
     isValidJsonObject
   );
 };
 
+export const isValidEnum = (value?: string) => {
+  try {
+    if (!value) return false;
+    const trimmedValue = value.trim();
+    if (
+      trimmedValue.indexOf('enum') === 0 &&
+      trimmedValue.lastIndexOf('}') === trimmedValue.length - 1
+    ) {
+      return true;
+    }
+  } catch {
+    // do nothing
+  }
+  return false;
+};
+
+const isEnum = (message?: string) => {
+  return yup.string().test(
+    'isEnum',
+    // eslint-disable-next-line no-template-curly-in-string
+    message || '${path} is not Enum object',
+    isValidEnum
+  );
+};
+
 /**
  * due to yup rerunning all the object validiation during any render,
  * it makes sense to cache the async results
@@ -62,6 +88,7 @@ export function cacheTest(
 }
 
 yup.addMethod(yup.StringSchema, 'isJsonObject', isJsonObject);
+yup.addMethod(yup.StringSchema, 'isEnum', isEnum);
 
 export const topicFormValidationSchema = yup.object().shape({
   name: yup


Test patch: