Repository: provectus/kafka-ui
Issue: Fix sonar code smell warnings 
Labels: scope/backend


Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamOptions.tsx b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamOptions.tsx
deleted file mode 100644
index ae3aca7fb90..00000000000
--- a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamOptions.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react';
-import { TopicConfigOption } from 'redux/interfaces';
-import { omitBy } from 'lodash';
-import CUSTOM_PARAMS_OPTIONS from './customParamsOptions';
-
-interface Props {
-  existingFields: string[];
-}
-
-const CustomParamOptions: React.FC<Props> = ({ existingFields }) => {
-  const fields = omitBy(Object.values(CUSTOM_PARAMS_OPTIONS), (field) =>
-    existingFields.includes(field.name)
-  );
-
-  return (
-    <>
-      <option value="">Select</option>
-      {Object.values(fields).map((opt: TopicConfigOption) => (
-        <option key={opt.name} value={opt.name}>
-          {opt.name}
-        </option>
-      ))}
-    </>
-  );
-};
-
-export default React.memo(CustomParamOptions);
diff --git a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamSelect.tsx b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamSelect.tsx
index 143984d6b7a..2be0fd1783e 100644
--- a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamSelect.tsx
+++ b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamSelect.tsx
@@ -2,10 +2,10 @@ import React from 'react';
 import { useFormContext } from 'react-hook-form';
 import { TopicConfigValue } from 'redux/interfaces';
 import { ErrorMessage } from '@hookform/error-message';
-import CustomParamOptions from './CustomParamOptions';
+import { TOPIC_CUSTOM_PARAMS } from 'lib/constants';
 import { INDEX_PREFIX } from './CustomParams';
 
-interface Props {
+export interface CustomParamSelectProps {
   isDisabled: boolean;
   index: string;
   name: string;
@@ -13,7 +13,7 @@ interface Props {
   onNameChange: (inputName: string, name: string) => void;
 }
 
-const CustomParamSelect: React.FC<Props> = ({
+const CustomParamSelect: React.FC<CustomParamSelectProps> = ({
   isDisabled,
   index,
   name,
@@ -59,7 +59,16 @@ const CustomParamSelect: React.FC<Props> = ({
           disabled={isDisabled}
           defaultValue={name}
         >
-          <CustomParamOptions existingFields={existingFields} />
+          <option value="">Select</option>
+          {Object.keys(TOPIC_CUSTOM_PARAMS).map((opt) => (
+            <option
+              key={opt}
+              value={opt}
+              disabled={existingFields.includes(opt)}
+            >
+              {opt}
+            </option>
+          ))}
         </select>
         <p className="help is-danger">
           <ErrorMessage errors={errors} name={optInputName} />
diff --git a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamValue.tsx b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamValue.tsx
index af98f870bb1..64e03a218e5 100644
--- a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamValue.tsx
+++ b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamValue.tsx
@@ -2,7 +2,7 @@ import React from 'react';
 import { useFormContext } from 'react-hook-form';
 import { TopicConfig } from 'generated-sources';
 import { ErrorMessage } from '@hookform/error-message';
-import CUSTOM_PARAMS_OPTIONS from './customParamsOptions';
+import { TOPIC_CUSTOM_PARAMS } from 'lib/constants';
 
 interface Props {
   isDisabled: boolean;
@@ -24,10 +24,7 @@ const CustomParamValue: React.FC<Props> = ({
 
   React.useEffect(() => {
     if (selectedParamName && !defaultValue) {
-      setValue(
-        valInputName,
-        CUSTOM_PARAMS_OPTIONS[selectedParamName].defaultValue
-      );
+      setValue(valInputName, TOPIC_CUSTOM_PARAMS[selectedParamName]);
     }
   }, [selectedParamName, setValue, valInputName]);
 
diff --git a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__tests__/CustomParamSelect.spec.tsx b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__tests__/CustomParamSelect.spec.tsx
new file mode 100644
index 00000000000..ce2ddbe476e
--- /dev/null
+++ b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__tests__/CustomParamSelect.spec.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { useForm, FormProvider, useFormContext } from 'react-hook-form';
+import { TOPIC_CUSTOM_PARAMS } from 'lib/constants';
+import CustomParamSelect, {
+  CustomParamSelectProps,
+} from '../CustomParamSelect';
+
+const existingFields = [
+  'leader.replication.throttled.replicas',
+  'segment.index.bytes',
+  'message.timestamp.difference.max.ms',
+];
+
+const Wrapper: React.FC<Partial<CustomParamSelectProps>> = (props = {}) => {
+  const methods = useForm();
+  return (
+    <FormProvider {...methods}>
+      <CustomParamSelect
+        index="1"
+        name="my_custom_param"
+        existingFields={[]}
+        isDisabled
+        onNameChange={jest.fn()}
+        {...props}
+      />
+    </FormProvider>
+  );
+};
+
+describe('CustomParamSelect', () => {
+  it('renders correct options', () => {
+    const fieldsCount = Object.keys(TOPIC_CUSTOM_PARAMS).length;
+    const wrapper = mount(<Wrapper existingFields={existingFields} />);
+    const options = wrapper.find('option');
+    const disabledOptions = options.filterWhere((o) => !!o.prop('disabled'));
+
+    expect(options.length).toEqual(fieldsCount + 1);
+    expect(disabledOptions.length).toEqual(existingFields.length);
+  });
+
+  it('matches snapshot', () => {
+    expect(
+      mount(<Wrapper existingFields={existingFields} />).find(
+        'Memo(CustomParamSelect)'
+      )
+    ).toMatchSnapshot();
+  });
+});
diff --git a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__tests__/__snapshots__/CustomParamSelect.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__tests__/__snapshots__/CustomParamSelect.spec.tsx.snap
new file mode 100644
index 00000000000..dab5c8e1107
--- /dev/null
+++ b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__tests__/__snapshots__/CustomParamSelect.spec.tsx.snap
@@ -0,0 +1,201 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`CustomParamSelect matches snapshot 1`] = `
+<Memo(CustomParamSelect)
+  existingFields={
+    Array [
+      "leader.replication.throttled.replicas",
+      "segment.index.bytes",
+      "message.timestamp.difference.max.ms",
+    ]
+  }
+  index="1"
+  isDisabled={true}
+  name="my_custom_param"
+  onNameChange={[MockFunction]}
+>
+  <label
+    className="label"
+  >
+    Custom Parameter
+  </label>
+  <div
+    className="select is-block"
+  >
+    <select
+      defaultValue="my_custom_param"
+      disabled={true}
+      name="1[name]"
+      onChange={[Function]}
+    >
+      <option
+        value=""
+      >
+        Select
+      </option>
+      <option
+        disabled={false}
+        key="compression.type"
+        value="compression.type"
+      >
+        compression.type
+      </option>
+      <option
+        disabled={true}
+        key="leader.replication.throttled.replicas"
+        value="leader.replication.throttled.replicas"
+      >
+        leader.replication.throttled.replicas
+      </option>
+      <option
+        disabled={false}
+        key="message.downconversion.enable"
+        value="message.downconversion.enable"
+      >
+        message.downconversion.enable
+      </option>
+      <option
+        disabled={false}
+        key="segment.jitter.ms"
+        value="segment.jitter.ms"
+      >
+        segment.jitter.ms
+      </option>
+      <option
+        disabled={false}
+        key="flush.ms"
+        value="flush.ms"
+      >
+        flush.ms
+      </option>
+      <option
+        disabled={false}
+        key="follower.replication.throttled.replicas"
+        value="follower.replication.throttled.replicas"
+      >
+        follower.replication.throttled.replicas
+      </option>
+      <option
+        disabled={false}
+        key="segment.bytes"
+        value="segment.bytes"
+      >
+        segment.bytes
+      </option>
+      <option
+        disabled={false}
+        key="flush.messages"
+        value="flush.messages"
+      >
+        flush.messages
+      </option>
+      <option
+        disabled={false}
+        key="message.format.version"
+        value="message.format.version"
+      >
+        message.format.version
+      </option>
+      <option
+        disabled={false}
+        key="file.delete.delay.ms"
+        value="file.delete.delay.ms"
+      >
+        file.delete.delay.ms
+      </option>
+      <option
+        disabled={false}
+        key="max.compaction.lag.ms"
+        value="max.compaction.lag.ms"
+      >
+        max.compaction.lag.ms
+      </option>
+      <option
+        disabled={false}
+        key="min.compaction.lag.ms"
+        value="min.compaction.lag.ms"
+      >
+        min.compaction.lag.ms
+      </option>
+      <option
+        disabled={false}
+        key="message.timestamp.type"
+        value="message.timestamp.type"
+      >
+        message.timestamp.type
+      </option>
+      <option
+        disabled={false}
+        key="preallocate"
+        value="preallocate"
+      >
+        preallocate
+      </option>
+      <option
+        disabled={false}
+        key="min.cleanable.dirty.ratio"
+        value="min.cleanable.dirty.ratio"
+      >
+        min.cleanable.dirty.ratio
+      </option>
+      <option
+        disabled={false}
+        key="index.interval.bytes"
+        value="index.interval.bytes"
+      >
+        index.interval.bytes
+      </option>
+      <option
+        disabled={false}
+        key="unclean.leader.election.enable"
+        value="unclean.leader.election.enable"
+      >
+        unclean.leader.election.enable
+      </option>
+      <option
+        disabled={false}
+        key="retention.bytes"
+        value="retention.bytes"
+      >
+        retention.bytes
+      </option>
+      <option
+        disabled={false}
+        key="delete.retention.ms"
+        value="delete.retention.ms"
+      >
+        delete.retention.ms
+      </option>
+      <option
+        disabled={false}
+        key="segment.ms"
+        value="segment.ms"
+      >
+        segment.ms
+      </option>
+      <option
+        disabled={true}
+        key="message.timestamp.difference.max.ms"
+        value="message.timestamp.difference.max.ms"
+      >
+        message.timestamp.difference.max.ms
+      </option>
+      <option
+        disabled={true}
+        key="segment.index.bytes"
+        value="segment.index.bytes"
+      >
+        segment.index.bytes
+      </option>
+    </select>
+    <p
+      className="help is-danger"
+    >
+      <Component
+        errors={Object {}}
+        name="1[name]"
+      />
+    </p>
+  </div>
+</Memo(CustomParamSelect)>
+`;
diff --git a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/customParamsOptions.tsx b/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/customParamsOptions.tsx
deleted file mode 100644
index 4613baeecb5..00000000000
--- a/kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/customParamsOptions.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { TopicConfigOption } from 'redux/interfaces';
-
-interface TopicConfigOptions {
-  [optionName: string]: TopicConfigOption;
-}
-
-const CUSTOM_PARAMS_OPTIONS: TopicConfigOptions = {
-  'compression.type': {
-    name: 'compression.type',
-    defaultValue: 'producer',
-  },
-  'leader.replication.throttled.replicas': {
-    name: 'leader.replication.throttled.replicas',
-    defaultValue: '',
-  },
-  'message.downconversion.enable': {
-    name: 'message.downconversion.enable',
-    defaultValue: 'true',
-  },
-  'segment.jitter.ms': {
-    name: 'segment.jitter.ms',
-    defaultValue: '0',
-  },
-  'flush.ms': {
-    name: 'flush.ms',
-    defaultValue: '9223372036854775807',
-  },
-  'follower.replication.throttled.replicas': {
-    name: 'follower.replication.throttled.replicas',
-    defaultValue: '',
-  },
-  'segment.bytes': {
-    name: 'segment.bytes',
-    defaultValue: '1073741824',
-  },
-  'flush.messages': {
-    name: 'flush.messages',
-    defaultValue: '9223372036854775807',
-  },
-  'message.format.version': {
-    name: 'message.format.version',
-    defaultValue: '2.3-IV1',
-  },
-  'file.delete.delay.ms': {
-    name: 'file.delete.delay.ms',
-    defaultValue: '60000',
-  },
-  'max.compaction.lag.ms': {
-    name: 'max.compaction.lag.ms',
-    defaultValue: '9223372036854775807',
-  },
-  'min.compaction.lag.ms': {
-    name: 'min.compaction.lag.ms',
-    defaultValue: '0',
-  },
-  'message.timestamp.type': {
-    name: 'message.timestamp.type',
-    defaultValue: 'CreateTime',
-  },
-  preallocate: {
-    name: 'preallocate',
-    defaultValue: 'false',
-  },
-  'min.cleanable.dirty.ratio': {
-    name: 'min.cleanable.dirty.ratio',
-    defaultValue: '0.5',
-  },
-  'index.interval.bytes': {
-    name: 'index.interval.bytes',
-    defaultValue: '4096',
-  },
-  'unclean.leader.election.enable': {
-    name: 'unclean.leader.election.enable',
-    defaultValue: 'true',
-  },
-  'retention.bytes': {
-    name: 'retention.bytes',
-    defaultValue: '-1',
-  },
-  'delete.retention.ms': {
-    name: 'delete.retention.ms',
-    defaultValue: '86400000',
-  },
-  'segment.ms': {
-    name: 'segment.ms',
-    defaultValue: '604800000',
-  },
-  'message.timestamp.difference.max.ms': {
-    name: 'message.timestamp.difference.max.ms',
-    defaultValue: '9223372036854775807',
-  },
-  'segment.index.bytes': {
-    name: 'segment.index.bytes',
-    defaultValue: '10485760',
-  },
-};
-
-export default CUSTOM_PARAMS_OPTIONS;
diff --git a/kafka-ui-react-app/src/lib/constants.ts b/kafka-ui-react-app/src/lib/constants.ts
index 53bf47144e8..76de060f45f 100644
--- a/kafka-ui-react-app/src/lib/constants.ts
+++ b/kafka-ui-react-app/src/lib/constants.ts
@@ -11,6 +11,31 @@ export const BASE_PARAMS: ConfigurationParameters = {
 export const TOPIC_NAME_VALIDATION_PATTERN = RegExp(/^[.,A-Za-z0-9_-]+$/);
 export const SCHEMA_NAME_VALIDATION_PATTERN = RegExp(/^[.,A-Za-z0-9_-]+$/);
 
+export const TOPIC_CUSTOM_PARAMS: Record<string, string> = {
+  'compression.type': 'producer',
+  'leader.replication.throttled.replicas': '',
+  'message.downconversion.enable': 'true',
+  'segment.jitter.ms': '0',
+  'flush.ms': '9223372036854775807',
+  'follower.replication.throttled.replicas': '',
+  'segment.bytes': '1073741824',
+  'flush.messages': '9223372036854775807',
+  'message.format.version': '2.3-IV1',
+  'file.delete.delay.ms': '60000',
+  'max.compaction.lag.ms': '9223372036854775807',
+  'min.compaction.lag.ms': '0',
+  'message.timestamp.type': 'CreateTime',
+  preallocate: 'false',
+  'min.cleanable.dirty.ratio': '0.5',
+  'index.interval.bytes': '4096',
+  'unclean.leader.election.enable': 'true',
+  'retention.bytes': '-1',
+  'delete.retention.ms': '86400000',
+  'segment.ms': '604800000',
+  'message.timestamp.difference.max.ms': '9223372036854775807',
+  'segment.index.bytes': '10485760',
+};
+
 export const MILLISECONDS_IN_WEEK = 604_800_000;
 export const MILLISECONDS_IN_DAY = 86_400_000;
 export const MILLISECONDS_IN_SECOND = 1_000;
diff --git a/kafka-ui-react-app/src/redux/interfaces/topic.ts b/kafka-ui-react-app/src/redux/interfaces/topic.ts
index d7c2ee99fcb..16968241f6a 100644
--- a/kafka-ui-react-app/src/redux/interfaces/topic.ts
+++ b/kafka-ui-react-app/src/redux/interfaces/topic.ts
@@ -22,11 +22,6 @@ export interface TopicConfigParams {
   [paramName: string]: TopicConfig;
 }
 
-export interface TopicConfigOption {
-  name: TopicConfig['name'];
-  defaultValue: TopicConfig['defaultValue'];
-}
-
 export interface TopicConfigValue {
   name: TopicConfig['name'];
   value: TopicConfig['value'];


Test patch: