Skip to content

Commit d7ed00f

Browse files
Merge branch 'main' into issues/300-regex-for-rbac
2 parents aa989b1 + 8146e71 commit d7ed00f

File tree

11 files changed

+385
-11
lines changed

11 files changed

+385
-11
lines changed

.github/workflows/frontend_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
ref: ${{ github.event.pull_request.head.sha }}
2222
token: ${{ github.token }}
2323

24-
- uses: pnpm/action-setup@v4.0.0
24+
- uses: pnpm/action-setup@v4.1.0
2525
with:
2626
version: 9.15.4
2727

api/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ plugins {
77
alias(libs.plugins.spring.dependency.management)
88
}
99

10+
1011
import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage
1112

1213
dependencies {
@@ -120,6 +121,10 @@ test {
120121
useJUnitPlatform()
121122
}
122123

124+
springBoot {
125+
buildInfo()
126+
}
127+
123128
tasks.register('buildDockerImageTask', DockerBuildImage) {
124129
inputDir = projectDir
125130
dockerFile = project.layout.projectDirectory.file('Dockerfile')

frontend/src/lib/constants.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { ConfigurationParameters, ConsumerGroupState } from 'generated-sources';
1+
import {
2+
ApplicationConfigPropertiesKafkaMaskingTypeEnum,
3+
ConfigurationParameters,
4+
ConsumerGroupState,
5+
} from 'generated-sources';
26

37
declare global {
48
interface Window {
@@ -101,6 +105,20 @@ export const METRICS_OPTIONS = [
101105
{ value: 'JMX', label: 'JMX' },
102106
{ value: 'PROMETHEUS', label: 'PROMETHEUS' },
103107
];
108+
export const MASKING_OPTIONS = [
109+
{
110+
value: ApplicationConfigPropertiesKafkaMaskingTypeEnum.MASK,
111+
label: 'MASK',
112+
},
113+
{
114+
value: ApplicationConfigPropertiesKafkaMaskingTypeEnum.REMOVE,
115+
label: 'REMOVE',
116+
},
117+
{
118+
value: ApplicationConfigPropertiesKafkaMaskingTypeEnum.REPLACE,
119+
label: 'REPLACE',
120+
},
121+
];
104122

105123
export const CONSUMER_GROUP_STATE_TOOLTIPS: Record<ConsumerGroupState, string> =
106124
{

frontend/src/widgets/ClusterConfigForm/ClusterConfigForm.styled.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const FlexGrow1 = styled.div`
4141
flex-direction: column;
4242
display: flex;
4343
`;
44+
4445
// KafkaCluster
4546
export const BootstrapServer = styled(InputContainer)`
4647
grid-template-columns: 3fr 110px 30px;
@@ -58,5 +59,23 @@ export const FileUploadInputWrapper = styled.div`
5859
display: flex;
5960
height: 40px;
6061
align-items: center;
61-
color: ${({ theme }) => theme.clusterConfigForm.fileInput.color}};
62+
color: ${({ theme }) => theme.clusterConfigForm.fileInput.color};
63+
`;
64+
65+
// Masking
66+
export const FieldWrapper = styled.div`
67+
display: flex;
68+
gap: 8px;
69+
align-items: center;
70+
flex-wrap: wrap;
71+
`;
72+
export const FieldContainer = styled.div`
73+
display: flex;
74+
flex-direction: row;
75+
gap: 8px;
76+
align-items: center;
77+
`;
78+
export const Error = styled.p`
79+
color: ${({ theme }) => theme.input.error};
80+
font-size: 12px;
6281
`;
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
import * as React from 'react';
2+
import * as S from 'widgets/ClusterConfigForm/ClusterConfigForm.styled';
3+
import { Button } from 'components/common/Button/Button';
4+
import Input from 'components/common/Input/Input';
5+
import { useFieldArray, useFormContext } from 'react-hook-form';
6+
import PlusIcon from 'components/common/Icons/PlusIcon';
7+
import IconButtonWrapper from 'components/common/Icons/IconButtonWrapper';
8+
import CloseCircleIcon from 'components/common/Icons/CloseCircleIcon';
9+
import {
10+
FieldContainer,
11+
FieldWrapper,
12+
FlexGrow1,
13+
FlexRow,
14+
} from 'widgets/ClusterConfigForm/ClusterConfigForm.styled';
15+
import SectionHeader from 'widgets/ClusterConfigForm/common/SectionHeader';
16+
import { MASKING_OPTIONS } from 'lib/constants';
17+
import ControlledSelect from 'components/common/Select/ControlledSelect';
18+
import { FormError } from 'components/common/Input/Input.styled';
19+
import { ErrorMessage } from '@hookform/error-message';
20+
21+
const Fields = ({ nestedIdx }: { nestedIdx: number }) => {
22+
const { control } = useFormContext();
23+
const { fields, append, remove } = useFieldArray({
24+
control,
25+
name: `masking.${nestedIdx}.fields`,
26+
});
27+
28+
const handleAppend = () => append({ value: '' });
29+
30+
return (
31+
<FlexGrow1>
32+
<FieldWrapper>
33+
<FieldWrapper>
34+
{fields.map((item, index) => (
35+
<FieldContainer key={item.id}>
36+
<Input
37+
label="Field"
38+
name={`masking.${nestedIdx}.fields.${index}.value`}
39+
placeholder="Field"
40+
type="text"
41+
withError
42+
/>
43+
44+
{fields.length > 1 && (
45+
<S.RemoveButton
46+
style={{ marginTop: '18px' }}
47+
onClick={() => remove(index)}
48+
>
49+
<IconButtonWrapper aria-label="deleteProperty">
50+
<CloseCircleIcon aria-hidden />
51+
</IconButtonWrapper>
52+
</S.RemoveButton>
53+
)}
54+
</FieldContainer>
55+
))}
56+
</FieldWrapper>
57+
58+
<Button
59+
style={{ marginTop: '20px' }}
60+
type="button"
61+
buttonSize="M"
62+
buttonType="secondary"
63+
onClick={handleAppend}
64+
>
65+
<PlusIcon />
66+
Add Field
67+
</Button>
68+
</FieldWrapper>
69+
70+
<FormError>
71+
<ErrorMessage name={`masking.${nestedIdx}.fields`} />
72+
</FormError>
73+
</FlexGrow1>
74+
);
75+
};
76+
77+
const MaskingCharReplacement = ({ nestedIdx }: { nestedIdx: number }) => {
78+
const { control } = useFormContext();
79+
const { fields, append, remove } = useFieldArray({
80+
control,
81+
name: `masking.${nestedIdx}.maskingCharsReplacement`,
82+
});
83+
84+
const handleAppend = () => append({ value: '' });
85+
86+
return (
87+
<FlexGrow1>
88+
<FieldWrapper>
89+
<FieldWrapper>
90+
{fields.map((item, index) => (
91+
<FieldContainer key={item.id}>
92+
<Input
93+
label="Field"
94+
name={`masking.${nestedIdx}.maskingCharsReplacement.${index}.value`}
95+
placeholder="Field"
96+
type="text"
97+
withError
98+
/>
99+
100+
{fields.length > 1 && (
101+
<S.RemoveButton
102+
style={{ marginTop: '18px' }}
103+
onClick={() => remove(index)}
104+
>
105+
<IconButtonWrapper aria-label="deleteProperty">
106+
<CloseCircleIcon aria-hidden />
107+
</IconButtonWrapper>
108+
</S.RemoveButton>
109+
)}
110+
</FieldContainer>
111+
))}
112+
</FieldWrapper>
113+
114+
<Button
115+
style={{ marginTop: '20px' }}
116+
type="button"
117+
buttonSize="M"
118+
buttonType="secondary"
119+
onClick={handleAppend}
120+
>
121+
<PlusIcon />
122+
Add Masking Chars Replacement
123+
</Button>
124+
</FieldWrapper>
125+
126+
<FormError>
127+
<ErrorMessage name={`masking.${nestedIdx}.maskingCharsReplacement`} />
128+
</FormError>
129+
</FlexGrow1>
130+
);
131+
};
132+
133+
const Masking = () => {
134+
const { control } = useFormContext();
135+
const { fields, append, remove } = useFieldArray({
136+
control,
137+
name: 'masking',
138+
});
139+
const handleAppend = () =>
140+
append({
141+
type: undefined,
142+
fields: [{ value: '' }],
143+
fieldsNamePattern: '',
144+
maskingCharsReplacement: [{ value: '' }],
145+
replacement: '',
146+
topicKeysPattern: '',
147+
topicValuesPattern: '',
148+
});
149+
const toggleConfig = () => (fields.length === 0 ? handleAppend() : remove());
150+
151+
const hasFields = fields.length > 0;
152+
153+
return (
154+
<>
155+
<SectionHeader
156+
title="Masking"
157+
addButtonText="Configure Masking"
158+
adding={!hasFields}
159+
onClick={toggleConfig}
160+
/>
161+
{hasFields && (
162+
<S.GroupFieldWrapper>
163+
{fields.map((item, index) => (
164+
<div key={item.id}>
165+
<FlexRow>
166+
<FlexGrow1>
167+
<ControlledSelect
168+
name={`masking.${index}.type`}
169+
label="Masking Type *"
170+
placeholder="Choose masking type"
171+
options={MASKING_OPTIONS}
172+
/>
173+
<Fields nestedIdx={index} />
174+
<Input
175+
label="Fields name pattern"
176+
name={`masking.${index}.fieldsNamePattern`}
177+
placeholder="Pattern"
178+
type="text"
179+
withError
180+
/>
181+
<MaskingCharReplacement nestedIdx={index} />
182+
<Input
183+
label="Replacement"
184+
name={`masking.${index}.replacement`}
185+
placeholder="Replacement"
186+
type="text"
187+
/>
188+
<Input
189+
label="Topic Keys Pattern"
190+
name={`masking.${index}.topicKeysPattern`}
191+
placeholder="Keys pattern"
192+
type="text"
193+
/>
194+
<Input
195+
label="Topic Values Pattern"
196+
name={`masking.${index}.topicValuesPattern`}
197+
placeholder="Values pattern"
198+
type="text"
199+
/>
200+
</FlexGrow1>
201+
<S.RemoveButton onClick={() => remove(index)}>
202+
<IconButtonWrapper aria-label="deleteProperty">
203+
<CloseCircleIcon aria-hidden />
204+
</IconButtonWrapper>
205+
</S.RemoveButton>
206+
</FlexRow>
207+
208+
<hr />
209+
</div>
210+
))}
211+
<Button
212+
type="button"
213+
buttonSize="M"
214+
buttonType="secondary"
215+
onClick={handleAppend}
216+
>
217+
<PlusIcon />
218+
Add Masking
219+
</Button>
220+
</S.GroupFieldWrapper>
221+
)}
222+
</>
223+
);
224+
};
225+
export default Masking;

frontend/src/widgets/ClusterConfigForm/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Metrics from 'widgets/ClusterConfigForm/Sections/Metrics';
2222
import CustomAuthentication from 'widgets/ClusterConfigForm/Sections/CustomAuthentication';
2323
import Authentication from 'widgets/ClusterConfigForm/Sections/Authentication/Authentication';
2424
import KSQL from 'widgets/ClusterConfigForm/Sections/KSQL';
25+
import Masking from 'widgets/ClusterConfigForm/Sections/Masking';
2526
import { useConfirm } from 'lib/hooks/useConfirm';
2627

2728
interface ClusterConfigFormProps {
@@ -145,6 +146,8 @@ const ClusterConfigForm: React.FC<ClusterConfigFormProps> = ({
145146
<hr />
146147
<Metrics />
147148
<hr />
149+
<Masking />
150+
<hr />
148151
<S.ButtonWrapper>
149152
<Button
150153
buttonSize="L"

0 commit comments

Comments
 (0)