- Fixes No originally selected custom parameters in topic's edit tab #1534 - Adds test case for CustomParams to verify fix - Updates CustomParamsField to use predefined value - Renames INDEX_PREFIX to TOPIC_CUSTOM_PARAMS_PREFIX and moves it to constants file - Removes unused configs from Topic/Edit component - Rewrites DangerZone styled's - Rewrites DangerZone tests - Adds margin to DangerZone to match TopicForm width - Adds simple Topic/Edit test - Changes sonar-project.properties file's sonar.exclusions to correctly ignore paths Signed-off-by: Roman Zabaluev <rzabaluev@provectus.com> Co-authored-by: Roman Zabaluev <rzabaluev@provectus.com>
This commit is contained in:
parent
cf45ee0198
commit
ced74ac550
18 changed files with 789 additions and 499 deletions
2
.github/workflows/backend.yml
vendored
2
.github/workflows/backend.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
|||
- name: compose app
|
||||
id: step_five
|
||||
run: |
|
||||
docker-compose -f ./docker/kafka-ui.yaml up -d
|
||||
docker-compose -f ./documentation/compose/kafka-ui.yaml up -d
|
||||
- name: Set up JDK 1.13
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
|
|
2
.github/workflows/e2e-checks.yaml
vendored
2
.github/workflows/e2e-checks.yaml
vendored
|
@ -39,7 +39,7 @@ jobs:
|
|||
id: compose_app
|
||||
# use the following command until #819 will be fixed
|
||||
run: |
|
||||
docker-compose -f ./docker/kafka-ui-connectors.yaml up -d
|
||||
docker-compose -f ./documentation/compose/kafka-ui-connectors.yaml up -d
|
||||
- name: e2e run
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
|
|
|
@ -2,7 +2,7 @@ sonar.projectKey=provectus_kafka-ui_frontend
|
|||
sonar.organization=provectus
|
||||
|
||||
sonar.sources=.
|
||||
sonar.exclusions="**/__test?__/**,src/setupWorker.ts,src/setupTests.ts,**/fixtures.ts,src/lib/testHelpers.tsx"
|
||||
sonar.exclusions=**/__test?__/**,src/setupWorker.ts,src/setupTests.ts,**/fixtures.ts,src/lib/testHelpers.tsx
|
||||
|
||||
sonar.typescript.lcov.reportPaths=./coverage/lcov.info
|
||||
sonar.testExecutionReportPaths=./test-report.xml
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const DangerZoneWrapperStyled = styled.div`
|
||||
margin-top: 16px;
|
||||
export const Wrapper = styled.div`
|
||||
margin: 16px;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid ${({ theme }) => theme.dangerZone.borderColor};
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
|
@ -15,13 +14,13 @@ export const DangerZoneWrapperStyled = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
export const DangerZoneTitleStyled = styled.h1`
|
||||
export const Title = styled.h1`
|
||||
color: ${({ theme }) => theme.dangerZone.color};
|
||||
font-size: 20px;
|
||||
padding-bottom: 16px;
|
||||
`;
|
||||
|
||||
export const DagerZoneFormStyled = styled.form`
|
||||
export const Form = styled.form`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 16px;
|
||||
|
|
|
@ -7,11 +7,7 @@ import { InputLabel } from 'components/common/Input/InputLabel.styled';
|
|||
import React from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
|
||||
import {
|
||||
DagerZoneFormStyled,
|
||||
DangerZoneTitleStyled,
|
||||
DangerZoneWrapperStyled,
|
||||
} from './DangerZone.styled';
|
||||
import * as S from './DangerZone.styled';
|
||||
|
||||
export interface Props {
|
||||
clusterName: string;
|
||||
|
@ -109,12 +105,13 @@ const DangerZone: React.FC<Props> = ({
|
|||
);
|
||||
};
|
||||
return (
|
||||
<DangerZoneWrapperStyled>
|
||||
<DangerZoneTitleStyled>Danger Zone</DangerZoneTitleStyled>
|
||||
<S.Wrapper>
|
||||
<S.Title>Danger Zone</S.Title>
|
||||
<div>
|
||||
<FormProvider {...partitionsMethods}>
|
||||
<DagerZoneFormStyled
|
||||
<S.Form
|
||||
onSubmit={partitionsMethods.handleSubmit(validatePartitions)}
|
||||
aria-label="Edit number of partitions"
|
||||
>
|
||||
<div>
|
||||
<InputLabel htmlFor="partitions">
|
||||
|
@ -137,12 +134,11 @@ const DangerZone: React.FC<Props> = ({
|
|||
buttonSize="M"
|
||||
type="submit"
|
||||
disabled={!partitionsMethods.formState.isDirty}
|
||||
data-testid="partitionsSubmit"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</DagerZoneFormStyled>
|
||||
</S.Form>
|
||||
</FormProvider>
|
||||
<FormError>
|
||||
<ErrorMessage
|
||||
|
@ -160,10 +156,11 @@ const DangerZone: React.FC<Props> = ({
|
|||
</ConfirmationModal>
|
||||
|
||||
<FormProvider {...replicationFactorMethods}>
|
||||
<DagerZoneFormStyled
|
||||
<S.Form
|
||||
onSubmit={replicationFactorMethods.handleSubmit(
|
||||
validateReplicationFactor
|
||||
)}
|
||||
aria-label="Edit replication factor"
|
||||
>
|
||||
<div>
|
||||
<InputLabel htmlFor="replicationFactor">
|
||||
|
@ -185,12 +182,11 @@ const DangerZone: React.FC<Props> = ({
|
|||
buttonSize="M"
|
||||
type="submit"
|
||||
disabled={!replicationFactorMethods.formState.isDirty}
|
||||
data-testid="replicationFactorSubmit"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</DagerZoneFormStyled>
|
||||
</S.Form>
|
||||
</FormProvider>
|
||||
|
||||
<FormError>
|
||||
|
@ -207,7 +203,7 @@ const DangerZone: React.FC<Props> = ({
|
|||
Are you sure you want to update the replication factor?
|
||||
</ConfirmationModal>
|
||||
</div>
|
||||
</DangerZoneWrapperStyled>
|
||||
</S.Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -7,21 +7,19 @@ import {
|
|||
TopicWithDetailedInfo,
|
||||
TopicFormData,
|
||||
} from 'redux/interfaces';
|
||||
import { TopicConfig } from 'generated-sources';
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import { camelCase } from 'lodash';
|
||||
import TopicForm from 'components/Topics/shared/Form/TopicForm';
|
||||
import { clusterTopicPath } from 'lib/paths';
|
||||
import { useHistory } from 'react-router';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { topicFormValidationSchema } from 'lib/yupExtended';
|
||||
import { TOPIC_CUSTOM_PARAMS } from 'lib/constants';
|
||||
import { TOPIC_CUSTOM_PARAMS_PREFIX, TOPIC_CUSTOM_PARAMS } from 'lib/constants';
|
||||
import styled from 'styled-components';
|
||||
import PageHeading from 'components/common/PageHeading/PageHeading';
|
||||
|
||||
import DangerZoneContainer from './DangerZone/DangerZoneContainer';
|
||||
|
||||
interface Props {
|
||||
export interface Props {
|
||||
clusterName: ClusterName;
|
||||
topicName: TopicName;
|
||||
topic?: TopicWithDetailedInfo;
|
||||
|
@ -64,27 +62,18 @@ const topicParams = (topic: TopicWithDetailedInfo | undefined) => {
|
|||
|
||||
const { name, replicationFactor } = topic;
|
||||
|
||||
const configs = topic.config?.reduce(
|
||||
(result: { [key: string]: TopicConfig['value'] }, param) => ({
|
||||
...result,
|
||||
[camelCase(param.name)]: param.value || param.defaultValue,
|
||||
}),
|
||||
{}
|
||||
);
|
||||
|
||||
return {
|
||||
...DEFAULTS,
|
||||
name,
|
||||
partitions: topic.partitionCount || DEFAULTS.partitions,
|
||||
replicationFactor,
|
||||
customParams: topic.config
|
||||
[TOPIC_CUSTOM_PARAMS_PREFIX]: topic.config
|
||||
?.filter(
|
||||
(el) =>
|
||||
el.value !== el.defaultValue &&
|
||||
Object.keys(TOPIC_CUSTOM_PARAMS).includes(el.name)
|
||||
)
|
||||
.map((el) => ({ name: el.name, value: el.value })),
|
||||
...configs,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -99,8 +88,10 @@ const Edit: React.FC<Props> = ({
|
|||
fetchTopicConfig,
|
||||
updateTopic,
|
||||
}) => {
|
||||
const defaultValues = topicParams(topic);
|
||||
|
||||
const defaultValues = React.useMemo(
|
||||
() => topicParams(topic),
|
||||
[topicParams, topic]
|
||||
);
|
||||
const methods = useForm<TopicFormData>({
|
||||
defaultValues,
|
||||
resolver: yupResolver(topicFormValidationSchema),
|
||||
|
@ -146,18 +137,15 @@ const Edit: React.FC<Props> = ({
|
|||
<>
|
||||
<PageHeading text={`Edit ${topicName}`} />
|
||||
<EditWrapperStyled>
|
||||
<div>
|
||||
<div>
|
||||
<FormProvider {...methods}>
|
||||
<TopicForm
|
||||
topicName={topicName}
|
||||
config={config}
|
||||
isSubmitting={isSubmitting}
|
||||
isEditing
|
||||
onSubmit={methods.handleSubmit(onSubmit)}
|
||||
/>
|
||||
</FormProvider>
|
||||
</div>
|
||||
{topic && (
|
||||
<DangerZoneContainer
|
||||
defaultPartitions={defaultValues.partitions}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
import React from 'react';
|
||||
import DangerZone, {
|
||||
Props,
|
||||
} from 'components/Topics/Topic/Edit/DangerZone/DangerZone';
|
||||
import { screen, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render } from 'lib/testHelpers';
|
||||
|
||||
import { topicName, clusterName } from './fixtures';
|
||||
|
||||
const renderComponent = (props?: Partial<Props>) =>
|
||||
render(
|
||||
<DangerZone
|
||||
clusterName={clusterName}
|
||||
topicName={topicName}
|
||||
defaultPartitions={3}
|
||||
defaultReplicationFactor={3}
|
||||
partitionsCountIncreased={false}
|
||||
replicationFactorUpdated={false}
|
||||
updateTopicPartitionsCount={jest.fn()}
|
||||
updateTopicReplicationFactor={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
const clickOnDialogSubmitButton = () => {
|
||||
userEvent.click(
|
||||
within(screen.getByRole('dialog')).getByRole('button', {
|
||||
name: 'Submit',
|
||||
})
|
||||
);
|
||||
};
|
||||
describe('DangerZone', () => {
|
||||
it('renders', () => {
|
||||
renderComponent();
|
||||
|
||||
const numberOfPartitionsEditForm = screen.getByRole('form', {
|
||||
name: 'Edit number of partitions',
|
||||
});
|
||||
expect(numberOfPartitionsEditForm).toBeInTheDocument();
|
||||
expect(
|
||||
within(numberOfPartitionsEditForm).getByRole('spinbutton', {
|
||||
name: 'Number of partitions *',
|
||||
})
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(numberOfPartitionsEditForm).getByRole('button', { name: 'Submit' })
|
||||
).toBeInTheDocument();
|
||||
|
||||
const replicationFactorEditForm = screen.getByRole('form', {
|
||||
name: 'Edit replication factor',
|
||||
});
|
||||
expect(replicationFactorEditForm).toBeInTheDocument();
|
||||
expect(
|
||||
within(replicationFactorEditForm).getByRole('spinbutton', {
|
||||
name: 'Replication Factor *',
|
||||
})
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(replicationFactorEditForm).getByRole('button', { name: 'Submit' })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls updateTopicPartitionsCount', async () => {
|
||||
const mockUpdateTopicPartitionsCount = jest.fn();
|
||||
renderComponent({
|
||||
updateTopicPartitionsCount: mockUpdateTopicPartitionsCount,
|
||||
});
|
||||
const numberOfPartitionsEditForm = screen.getByRole('form', {
|
||||
name: 'Edit number of partitions',
|
||||
});
|
||||
|
||||
userEvent.type(
|
||||
within(numberOfPartitionsEditForm).getByRole('spinbutton'),
|
||||
'4'
|
||||
);
|
||||
userEvent.click(within(numberOfPartitionsEditForm).getByRole('button'));
|
||||
|
||||
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
||||
await waitFor(() => clickOnDialogSubmitButton());
|
||||
|
||||
expect(mockUpdateTopicPartitionsCount).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls updateTopicReplicationFactor', async () => {
|
||||
const mockUpdateTopicReplicationFactor = jest.fn();
|
||||
renderComponent({
|
||||
updateTopicReplicationFactor: mockUpdateTopicReplicationFactor,
|
||||
});
|
||||
|
||||
const replicationFactorEditForm = screen.getByRole('form', {
|
||||
name: 'Edit replication factor',
|
||||
});
|
||||
expect(
|
||||
within(replicationFactorEditForm).getByRole('spinbutton', {
|
||||
name: 'Replication Factor *',
|
||||
})
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(replicationFactorEditForm).getByRole('button', { name: 'Submit' })
|
||||
).toBeInTheDocument();
|
||||
|
||||
userEvent.type(
|
||||
within(replicationFactorEditForm).getByRole('spinbutton'),
|
||||
'4'
|
||||
);
|
||||
userEvent.click(within(replicationFactorEditForm).getByRole('button'));
|
||||
|
||||
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
||||
await waitFor(() => clickOnDialogSubmitButton());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateTopicReplicationFactor).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import Edit, { Props } from 'components/Topics/Topic/Edit/Edit';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { render } from 'lib/testHelpers';
|
||||
|
||||
import { topicName, clusterName, topicWithInfo } from './fixtures';
|
||||
|
||||
const renderComponent = (props?: Partial<Props>) =>
|
||||
render(
|
||||
<Edit
|
||||
clusterName={clusterName}
|
||||
topicName={topicName}
|
||||
topic={topicWithInfo}
|
||||
isFetched
|
||||
isTopicUpdated={false}
|
||||
fetchTopicConfig={jest.fn()}
|
||||
updateTopic={jest.fn()}
|
||||
updateTopicPartitionsCount={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
describe('DangerZone', () => {
|
||||
it('renders', () => {
|
||||
renderComponent();
|
||||
|
||||
expect(
|
||||
screen.getByRole('heading', { name: `Edit ${topicName}` })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('heading', { name: `Danger Zone` })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,553 @@
|
|||
import { CleanUpPolicy, ConfigSource, TopicConfig } from 'generated-sources';
|
||||
import { TopicWithDetailedInfo } from 'redux/interfaces/topic';
|
||||
|
||||
export const clusterName = 'testCluster';
|
||||
export const topicName = 'testTopic';
|
||||
|
||||
export const config: TopicConfig[] = [
|
||||
{
|
||||
name: 'compression.type',
|
||||
value: 'producer',
|
||||
defaultValue: 'producer',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'compression.type',
|
||||
value: 'producer',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
},
|
||||
{
|
||||
name: 'compression.type',
|
||||
value: 'producer',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confluent.value.schema.validation',
|
||||
value: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'leader.replication.throttled.replicas',
|
||||
value: '',
|
||||
defaultValue: '',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'confluent.key.subject.name.strategy',
|
||||
value: 'io.confluent.kafka.serializers.subject.TopicNameStrategy',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'message.downconversion.enable',
|
||||
value: 'true',
|
||||
defaultValue: 'true',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.message.downconversion.enable',
|
||||
value: 'true',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'min.insync.replicas',
|
||||
value: '1',
|
||||
defaultValue: '1',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'min.insync.replicas',
|
||||
value: '1',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
},
|
||||
{
|
||||
name: 'min.insync.replicas',
|
||||
value: '1',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'segment.jitter.ms',
|
||||
value: '0',
|
||||
defaultValue: '0',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'cleanup.policy',
|
||||
value: 'delete',
|
||||
defaultValue: 'delete',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'cleanup.policy',
|
||||
value: 'delete',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
},
|
||||
{
|
||||
name: 'log.cleanup.policy',
|
||||
value: 'delete',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'flush.ms',
|
||||
value: '9223372036854775807',
|
||||
defaultValue: '9223372036854775807',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'confluent.tier.local.hotset.ms',
|
||||
value: '86400000',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'confluent.tier.local.hotset.ms',
|
||||
value: '86400000',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'follower.replication.throttled.replicas',
|
||||
value: '',
|
||||
defaultValue: '',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'confluent.tier.local.hotset.bytes',
|
||||
value: '-1',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'confluent.tier.local.hotset.bytes',
|
||||
value: '-1',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confluent.value.subject.name.strategy',
|
||||
value: 'io.confluent.kafka.serializers.subject.TopicNameStrategy',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'segment.bytes',
|
||||
value: '1073741824',
|
||||
defaultValue: '1073741824',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.segment.bytes',
|
||||
value: '1073741824',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'retention.ms',
|
||||
value: '604800000',
|
||||
defaultValue: '604800000',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'retention.ms',
|
||||
value: '604800000',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'flush.messages',
|
||||
value: '9223372036854775807',
|
||||
defaultValue: '9223372036854775807',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.flush.interval.messages',
|
||||
value: '9223372036854775807',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confluent.tier.enable',
|
||||
value: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'confluent.tier.enable',
|
||||
value: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confluent.tier.segment.hotset.roll.min.bytes',
|
||||
value: '104857600',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'confluent.tier.segment.hotset.roll.min.bytes',
|
||||
value: '104857600',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confluent.segment.speculative.prefetch.enable',
|
||||
value: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'confluent.segment.speculative.prefetch.enable',
|
||||
value: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'message.format.version',
|
||||
value: '2.7-IV2',
|
||||
defaultValue: '2.7-IV2',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.message.format.version',
|
||||
value: '2.7-IV2',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'max.compaction.lag.ms',
|
||||
value: '9223372036854775807',
|
||||
defaultValue: '9223372036854775807',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.cleaner.max.compaction.lag.ms',
|
||||
value: '9223372036854775807',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'file.delete.delay.ms',
|
||||
value: '60000',
|
||||
defaultValue: '60000',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.segment.delete.delay.ms',
|
||||
value: '60000',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'max.message.bytes',
|
||||
value: '1000012',
|
||||
defaultValue: '1000012',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'max.message.bytes',
|
||||
value: '1000012',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
},
|
||||
{
|
||||
name: 'message.max.bytes',
|
||||
value: '1048588',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'min.compaction.lag.ms',
|
||||
value: '0',
|
||||
defaultValue: '0',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.cleaner.min.compaction.lag.ms',
|
||||
value: '0',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'message.timestamp.type',
|
||||
value: 'CreateTime',
|
||||
defaultValue: 'CreateTime',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.message.timestamp.type',
|
||||
value: 'CreateTime',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'preallocate',
|
||||
value: 'false',
|
||||
defaultValue: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.preallocate',
|
||||
value: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confluent.placement.constraints',
|
||||
value: '',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'min.cleanable.dirty.ratio',
|
||||
value: '0.5',
|
||||
defaultValue: '0.5',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.cleaner.min.cleanable.ratio',
|
||||
value: '0.5',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'index.interval.bytes',
|
||||
value: '4096',
|
||||
defaultValue: '4096',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.index.interval.bytes',
|
||||
value: '4096',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'unclean.leader.election.enable',
|
||||
value: 'false',
|
||||
defaultValue: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'unclean.leader.election.enable',
|
||||
value: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'retention.bytes',
|
||||
value: '-1',
|
||||
defaultValue: '-1',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'retention.bytes',
|
||||
value: '-1',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
},
|
||||
{
|
||||
name: 'log.retention.bytes',
|
||||
value: '-1',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'delete.retention.ms',
|
||||
value: '86400001',
|
||||
defaultValue: '86400000',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'delete.retention.ms',
|
||||
value: '86400001',
|
||||
source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
|
||||
},
|
||||
{
|
||||
name: 'log.cleaner.delete.retention.ms',
|
||||
value: '86400000',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confluent.prefer.tier.fetch.ms',
|
||||
value: '-1',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'confluent.prefer.tier.fetch.ms',
|
||||
value: '-1',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confluent.key.schema.validation',
|
||||
value: 'false',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'segment.ms',
|
||||
value: '604800000',
|
||||
defaultValue: '604800000',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [],
|
||||
},
|
||||
{
|
||||
name: 'message.timestamp.difference.max.ms',
|
||||
value: '9223372036854775807',
|
||||
defaultValue: '9223372036854775807',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.message.timestamp.difference.max.ms',
|
||||
value: '9223372036854775807',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'segment.index.bytes',
|
||||
value: '10485760',
|
||||
defaultValue: '10485760',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
isSensitive: false,
|
||||
isReadOnly: false,
|
||||
synonyms: [
|
||||
{
|
||||
name: 'log.index.size.max.bytes',
|
||||
value: '10485760',
|
||||
source: ConfigSource.DEFAULT_CONFIG,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const partitions = [
|
||||
{
|
||||
partition: 0,
|
||||
leader: 2,
|
||||
replicas: [
|
||||
{
|
||||
broker: 2,
|
||||
leader: false,
|
||||
inSync: true,
|
||||
},
|
||||
],
|
||||
offsetMax: 0,
|
||||
offsetMin: 0,
|
||||
},
|
||||
];
|
||||
|
||||
export const topicWithInfo: TopicWithDetailedInfo = {
|
||||
name: topicName,
|
||||
internal: false,
|
||||
partitionCount: 1,
|
||||
replicationFactor: 1,
|
||||
replicas: 1,
|
||||
inSyncReplicas: 1,
|
||||
segmentSize: 0,
|
||||
segmentCount: 1,
|
||||
underReplicatedPartitions: 0,
|
||||
cleanUpPolicy: CleanUpPolicy.DELETE,
|
||||
partitions,
|
||||
config,
|
||||
};
|
|
@ -1,64 +0,0 @@
|
|||
import React from 'react';
|
||||
import DangerZone, {
|
||||
Props,
|
||||
} from 'components/Topics/Topic/Edit/DangerZone/DangerZone';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render } from 'lib/testHelpers';
|
||||
|
||||
const setupWrapper = (props?: Partial<Props>) => (
|
||||
<DangerZone
|
||||
clusterName="testCluster"
|
||||
topicName="testTopic"
|
||||
defaultPartitions={3}
|
||||
defaultReplicationFactor={3}
|
||||
partitionsCountIncreased={false}
|
||||
replicationFactorUpdated={false}
|
||||
updateTopicPartitionsCount={jest.fn()}
|
||||
updateTopicReplicationFactor={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
describe('DangerZone', () => {
|
||||
it('is rendered properly', () => {
|
||||
const component = render(setupWrapper());
|
||||
expect(component.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('calls updateTopicPartitionsCount', async () => {
|
||||
const mockUpdateTopicPartitionsCount = jest.fn();
|
||||
render(
|
||||
setupWrapper({
|
||||
updateTopicPartitionsCount: mockUpdateTopicPartitionsCount,
|
||||
})
|
||||
);
|
||||
|
||||
userEvent.type(screen.getByLabelText('Number of partitions *'), '4');
|
||||
userEvent.click(screen.getByTestId('partitionsSubmit'));
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.click(screen.getAllByText('Submit')[1]);
|
||||
expect(mockUpdateTopicPartitionsCount).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls updateTopicReplicationFactor', async () => {
|
||||
const mockUpdateTopicReplicationFactor = jest.fn();
|
||||
render(
|
||||
setupWrapper({
|
||||
updateTopicReplicationFactor: mockUpdateTopicReplicationFactor,
|
||||
})
|
||||
);
|
||||
|
||||
userEvent.type(screen.getByLabelText('Replication Factor *'), '4');
|
||||
userEvent.click(screen.getByTestId('replicationFactorSubmit'));
|
||||
await waitFor(() => {
|
||||
userEvent.click(screen.getAllByText('Submit')[2]);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateTopicReplicationFactor).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,360 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DangerZone is rendered properly 1`] = `
|
||||
.c6 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
padding: 0px 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
background: #4F4FFF;
|
||||
color: #FFFFFF;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.c6:hover:enabled {
|
||||
background: #1717CF;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.c6:active:enabled {
|
||||
background: #1414B8;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.c6:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.c6 a {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.c6 i {
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
.c5 {
|
||||
border: 1px #ABB5BA solid;
|
||||
border-radius: 4px;
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
padding-left: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c5::-webkit-input-placeholder {
|
||||
color: #ABB5BA;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c5::-moz-placeholder {
|
||||
color: #ABB5BA;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c5:-ms-input-placeholder {
|
||||
color: #ABB5BA;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c5::placeholder {
|
||||
color: #ABB5BA;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c5:hover {
|
||||
border-color: #73848C;
|
||||
}
|
||||
|
||||
.c5:focus {
|
||||
outline: none;
|
||||
border-color: #454F54;
|
||||
}
|
||||
|
||||
.c5:focus::-webkit-input-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.c5:focus::-moz-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.c5:focus:-ms-input-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.c5:focus::placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.c5:disabled {
|
||||
color: #ABB5BA;
|
||||
border-color: #E3E6E8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.c5:read-only {
|
||||
color: #171A1C;
|
||||
border: none;
|
||||
background-color: #F1F2F3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.c5:-moz-read-only:focus::placeholder {
|
||||
color: #ABB5BA;
|
||||
}
|
||||
|
||||
.c5:read-only:focus::placeholder {
|
||||
color: #ABB5BA;
|
||||
}
|
||||
|
||||
.c8 {
|
||||
border: 1px #ABB5BA solid;
|
||||
border-radius: 4px;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
padding-left: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c8::-webkit-input-placeholder {
|
||||
color: #ABB5BA;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c8::-moz-placeholder {
|
||||
color: #ABB5BA;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c8:-ms-input-placeholder {
|
||||
color: #ABB5BA;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c8::placeholder {
|
||||
color: #ABB5BA;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c8:hover {
|
||||
border-color: #73848C;
|
||||
}
|
||||
|
||||
.c8:focus {
|
||||
outline: none;
|
||||
border-color: #454F54;
|
||||
}
|
||||
|
||||
.c8:focus::-webkit-input-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.c8:focus::-moz-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.c8:focus:-ms-input-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.c8:focus::placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.c8:disabled {
|
||||
color: #ABB5BA;
|
||||
border-color: #E3E6E8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.c8:read-only {
|
||||
color: #171A1C;
|
||||
border: none;
|
||||
background-color: #F1F2F3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.c8:-moz-read-only:focus::placeholder {
|
||||
color: #ABB5BA;
|
||||
}
|
||||
|
||||
.c8:read-only:focus::placeholder {
|
||||
color: #ABB5BA;
|
||||
}
|
||||
|
||||
.c7 {
|
||||
color: #E51A1A;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #454F54;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
margin-top: 16px;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #E3E6E8;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.c0 > div {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
color: #E51A1A;
|
||||
font-size: 20px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: flex-end;
|
||||
-webkit-box-align: flex-end;
|
||||
-ms-flex-align: flex-end;
|
||||
align-items: flex-end;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.c2 > *:first-child {
|
||||
-webkit-box-flex: 4;
|
||||
-webkit-flex-grow: 4;
|
||||
-ms-flex-positive: 4;
|
||||
flex-grow: 4;
|
||||
}
|
||||
|
||||
.c2 > *:last-child {
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex-grow: 1;
|
||||
-ms-flex-positive: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="c0"
|
||||
>
|
||||
<h1
|
||||
class="c1"
|
||||
>
|
||||
Danger Zone
|
||||
</h1>
|
||||
<div>
|
||||
<form
|
||||
class="c2"
|
||||
>
|
||||
<div>
|
||||
<label
|
||||
class="c3"
|
||||
for="partitions"
|
||||
>
|
||||
Number of partitions *
|
||||
</label>
|
||||
<div
|
||||
class="c4"
|
||||
>
|
||||
<input
|
||||
class="c5 c4"
|
||||
id="partitions"
|
||||
name="partitions"
|
||||
placeholder="Number of partitions"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="c6"
|
||||
data-testid="partitionsSubmit"
|
||||
disabled=""
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<p
|
||||
class="c7"
|
||||
/>
|
||||
<form
|
||||
class="c2"
|
||||
>
|
||||
<div>
|
||||
<label
|
||||
class="c3"
|
||||
for="replicationFactor"
|
||||
>
|
||||
Replication Factor *
|
||||
</label>
|
||||
<div
|
||||
class="c4"
|
||||
>
|
||||
<input
|
||||
class="c8 c4"
|
||||
id="replicationFactor"
|
||||
name="replicationFactor"
|
||||
placeholder="Replication Factor"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="c6"
|
||||
data-testid="replicationFactorSubmit"
|
||||
disabled=""
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<p
|
||||
class="c7"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
|
@ -64,13 +64,14 @@ const CustomParamField: React.FC<Props> = ({
|
|||
control={control}
|
||||
rules={{ required: 'Custom Parameter is required.' }}
|
||||
name={`customParams.${index}.name`}
|
||||
render={({ field: { name, onChange } }) => (
|
||||
render={({ field: { value, name, onChange } }) => (
|
||||
<Select
|
||||
name={name}
|
||||
placeholder="Select"
|
||||
disabled={isDisabled}
|
||||
minWidth="270px"
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
options={Object.keys(TOPIC_CUSTOM_PARAMS)
|
||||
.sort()
|
||||
.map((opt) => ({
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
import React from 'react';
|
||||
import { TopicConfigByName, TopicFormData } from 'redux/interfaces';
|
||||
import { TopicFormData } from 'redux/interfaces';
|
||||
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';
|
||||
import { Button } from 'components/common/Button/Button';
|
||||
import { TOPIC_CUSTOM_PARAMS_PREFIX } from 'lib/constants';
|
||||
|
||||
import CustomParamField from './CustomParamField';
|
||||
import * as S from './CustomParams.styled';
|
||||
|
||||
export const INDEX_PREFIX = 'customParams';
|
||||
|
||||
export interface CustomParamsProps {
|
||||
isSubmitting: boolean;
|
||||
config?: TopicConfigByName;
|
||||
}
|
||||
|
||||
const CustomParams: React.FC<CustomParamsProps> = ({ isSubmitting }) => {
|
||||
const { control } = useFormContext<TopicFormData>();
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
control,
|
||||
name: INDEX_PREFIX,
|
||||
name: TOPIC_CUSTOM_PARAMS_PREFIX,
|
||||
});
|
||||
const watchFieldArray = useWatch({
|
||||
control,
|
||||
name: INDEX_PREFIX,
|
||||
name: TOPIC_CUSTOM_PARAMS_PREFIX,
|
||||
defaultValue: fields,
|
||||
});
|
||||
const controlledFields = fields.map((field, index) => {
|
||||
|
|
|
@ -8,6 +8,8 @@ import { FormProvider, useForm } from 'react-hook-form';
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import { TOPIC_CUSTOM_PARAMS } from 'lib/constants';
|
||||
|
||||
import { defaultValues } from './fixtures';
|
||||
|
||||
const selectOption = async (listbox: HTMLElement, option: string) => {
|
||||
await waitFor(() => {
|
||||
userEvent.click(listbox);
|
||||
|
@ -44,10 +46,9 @@ const expectOptionAvailability = async (
|
|||
await waitFor(() => userEvent.click(listbox));
|
||||
};
|
||||
|
||||
describe('CustomParams', () => {
|
||||
const setupComponent = (props: CustomParamsProps) => {
|
||||
const renderComponent = (props: CustomParamsProps, defaults = {}) => {
|
||||
const Wrapper: React.FC = ({ children }) => {
|
||||
const methods = useForm();
|
||||
const methods = useForm({ defaultValues: defaults });
|
||||
return <FormProvider {...methods}>{children}</FormProvider>;
|
||||
};
|
||||
|
||||
|
@ -58,17 +59,31 @@ describe('CustomParams', () => {
|
|||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupComponent({ isSubmitting: false });
|
||||
});
|
||||
|
||||
describe('CustomParams', () => {
|
||||
it('renders with props', () => {
|
||||
renderComponent({ isSubmitting: false });
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveTextContent('Add Custom Parameter');
|
||||
});
|
||||
|
||||
it('has defaultValues when they are set', () => {
|
||||
renderComponent({ isSubmitting: false }, defaultValues);
|
||||
|
||||
expect(
|
||||
screen.getByRole('option', { name: defaultValues.customParams[0].name })
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('textbox')).toHaveValue(
|
||||
defaultValues.customParams[0].value
|
||||
);
|
||||
});
|
||||
|
||||
describe('works with user inputs correctly', () => {
|
||||
beforeEach(() => {
|
||||
renderComponent({ isSubmitting: false });
|
||||
});
|
||||
|
||||
it('button click creates custom param fieldset', async () => {
|
||||
const button = screen.getByRole('button');
|
||||
await waitFor(() => userEvent.click(button));
|
|
@ -0,0 +1,15 @@
|
|||
export const defaultValues = {
|
||||
partitions: 1,
|
||||
replicationFactor: 1,
|
||||
minInSyncReplicas: 1,
|
||||
cleanupPolicy: 'delete',
|
||||
retentionBytes: -1,
|
||||
maxMessageBytes: 1000012,
|
||||
name: 'TestCustomParamEdit',
|
||||
customParams: [
|
||||
{
|
||||
name: 'delete.retention.ms',
|
||||
value: '86400001',
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { NOT_SET, BYTES_IN_GB } from 'lib/constants';
|
||||
import { TopicName, TopicConfigByName } from 'redux/interfaces';
|
||||
import { TopicName } from 'redux/interfaces';
|
||||
import { ErrorMessage } from '@hookform/error-message';
|
||||
import Select, { SelectOption } from 'components/common/Select/Select';
|
||||
import Input from 'components/common/Input/Input';
|
||||
|
@ -16,7 +16,6 @@ import * as S from './TopicForm.styled';
|
|||
|
||||
export interface Props {
|
||||
topicName?: TopicName;
|
||||
config?: TopicConfigByName;
|
||||
isEditing?: boolean;
|
||||
isSubmitting: boolean;
|
||||
onSubmit: (e: React.BaseSyntheticEvent) => Promise<void>;
|
||||
|
@ -38,7 +37,6 @@ const RetentionBytesOptions: Array<SelectOption> = [
|
|||
|
||||
const TopicForm: React.FC<Props> = ({
|
||||
topicName,
|
||||
config,
|
||||
isEditing,
|
||||
isSubmitting,
|
||||
onSubmit,
|
||||
|
@ -197,7 +195,7 @@ const TopicForm: React.FC<Props> = ({
|
|||
</S.Column>
|
||||
|
||||
<S.CustomParamsHeading>Custom parameters</S.CustomParamsHeading>
|
||||
<CustomParamsContainer isSubmitting={isSubmitting} config={config} />
|
||||
<CustomParamsContainer isSubmitting={isSubmitting} />
|
||||
|
||||
<Button type="submit" buttonType="primary" buttonSize="L">
|
||||
Send
|
||||
|
|
|
@ -17,6 +17,7 @@ export const BASE_PARAMS: ConfigurationParameters = {
|
|||
export const TOPIC_NAME_VALIDATION_PATTERN = /^[.,A-Za-z0-9_-]+$/;
|
||||
export const SCHEMA_NAME_VALIDATION_PATTERN = /^[.,A-Za-z0-9_-]+$/;
|
||||
|
||||
export const TOPIC_CUSTOM_PARAMS_PREFIX = 'customParams';
|
||||
export const TOPIC_CUSTOM_PARAMS: Record<string, string> = {
|
||||
'compression.type': 'producer',
|
||||
'leader.replication.throttled.replicas': '',
|
||||
|
|
Loading…
Add table
Reference in a new issue