diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/ConsumerRecordDeserializer.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/ConsumerRecordDeserializer.java index 8c7a3024edf490eb49f8c2693f48990d21efebe4..f5b70180349da25726df541a93ca6286765fd460 100644 --- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/ConsumerRecordDeserializer.java +++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/ConsumerRecordDeserializer.java @@ -123,11 +123,11 @@ public class ConsumerRecordDeserializer { } private static Long getKeySize(ConsumerRecord consumerRecord) { - return consumerRecord.key() != null ? (long) consumerRecord.key().get().length : null; + return consumerRecord.key() != null ? (long) consumerRecord.serializedKeySize() : null; } private static Long getValueSize(ConsumerRecord consumerRecord) { - return consumerRecord.value() != null ? (long) consumerRecord.value().get().length : null; + return consumerRecord.value() != null ? (long) consumerRecord.serializedValueSize() : null; } private static int headerSize(Header header) { diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/SerdesInitializer.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/SerdesInitializer.java index 40ea320b2eb05491e2d65e3d4016bf47cb7542f9..66692894a61902a84e857b7530d13ae650f72d9a 100644 --- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/SerdesInitializer.java +++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/SerdesInitializer.java @@ -122,8 +122,6 @@ public class SerdesInitializer { registeredSerdes, Optional.ofNullable(clusterProperties.getDefaultKeySerde()) .map(name -> Preconditions.checkNotNull(registeredSerdes.get(name), "Default key serde not found")) - .or(() -> Optional.ofNullable(registeredSerdes.get(SchemaRegistrySerde.name()))) - .or(() -> Optional.ofNullable(registeredSerdes.get(ProtobufFileSerde.name()))) .orElse(null), Optional.ofNullable(clusterProperties.getDefaultValueSerde()) .map(name -> Preconditions.checkNotNull(registeredSerdes.get(name), "Default value serde not found")) diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/KafkaConnectService.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/KafkaConnectService.java index d07ef7ed2dd66211aa687e68a13cee5bcc81a377..98b61541c5f9bd91dd742fbc9c03d4863813318d 100644 --- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/KafkaConnectService.java +++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/KafkaConnectService.java @@ -109,6 +109,7 @@ public class KafkaConnectService { private Stream getStringsForSearch(FullConnectorInfoDTO fullConnectorInfo) { return Stream.of( fullConnectorInfo.getName(), + fullConnectorInfo.getConnect(), fullConnectorInfo.getStatus().getState().getValue(), fullConnectorInfo.getType().getValue()); } diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/analyze/TopicAnalysisStats.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/analyze/TopicAnalysisStats.java index d5b4400807d2fdb40ad196ef63e80d422c5f52f0..f36d3bec4deed6008eb8d012a48d58e448cd1b71 100644 --- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/analyze/TopicAnalysisStats.java +++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/analyze/TopicAnalysisStats.java @@ -43,8 +43,7 @@ class TopicAnalysisStats { Long max; final UpdateDoublesSketch sizeSketch = DoublesSketch.builder().build(); - void apply(byte[] bytes) { - int len = bytes.length; + void apply(int len) { sum += len; min = minNullable(min, len); max = maxNullable(max, len); @@ -98,7 +97,7 @@ class TopicAnalysisStats { if (rec.key() != null) { byte[] keyBytes = rec.key().get(); - keysSize.apply(keyBytes); + keysSize.apply(rec.serializedKeySize()); uniqKeys.update(keyBytes); } else { nullKeys++; @@ -106,7 +105,7 @@ class TopicAnalysisStats { if (rec.value() != null) { byte[] valueBytes = rec.value().get(); - valuesSize.apply(valueBytes); + valuesSize.apply(rec.serializedValueSize()); uniqValues.update(valueBytes); } else { nullValues++; diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Messages/Message.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Messages/Message.tsx index fb4e258cca55bf713256c34df6be6d05fe55339f..dd5cfae74885c11f087bce7b526ed5eaf596bed3 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/Messages/Message.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/Messages/Message.tsx @@ -142,6 +142,8 @@ const Message: React.FC = ({ timestampType={timestampType} keySize={keySize} contentSize={valueSize} + keySerde={keySerde} + valueSerde={valueSerde} /> )} diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx index 93616ca432a84da3d1831ba02115d11adfc0c610..d1237ba0d407ed0b5a94fe7d142f0bb908ff0554 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx @@ -3,7 +3,6 @@ import EditorViewer from 'components/common/EditorViewer/EditorViewer'; import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted'; import { SchemaType, TopicMessageTimestampTypeEnum } from 'generated-sources'; import { formatTimestamp } from 'lib/dateTimeHelpers'; -import { useSearchParams } from 'react-router-dom'; import * as S from './MessageContent.styled'; @@ -17,6 +16,8 @@ export interface MessageContentProps { timestampType?: TopicMessageTimestampTypeEnum; keySize?: number; contentSize?: number; + keySerde?: string; + valueSerde?: string; } const MessageContent: React.FC = ({ @@ -27,12 +28,10 @@ const MessageContent: React.FC = ({ timestampType, keySize, contentSize, + keySerde, + valueSerde, }) => { const [activeTab, setActiveTab] = React.useState('content'); - const [searchParams] = useSearchParams(); - const keyFormat = searchParams.get('keySerde') || ''; - const valueFormat = searchParams.get('valueSerde') || ''; - const activeTabContent = () => { switch (activeTab) { case 'content': @@ -110,7 +109,7 @@ const MessageContent: React.FC = ({ Key Serde - {keyFormat} + {keySerde} Size: @@ -120,7 +119,7 @@ const MessageContent: React.FC = ({ Value Serde - {valueFormat} + {valueSerde} Size: diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx index 91310a30e41379a31d63ae02e1906ba321162c85..d76455242cf491abfacac609628a1bf6c941e7da 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx @@ -20,6 +20,8 @@ const setupWrapper = (props?: Partial) => { headers={{ header: 'test' }} timestamp={new Date(0)} timestampType={TopicMessageTimestampTypeEnum.CREATE_TIME} + keySerde="SchemaRegistry" + valueSerde="Avro" {...props} /> @@ -27,42 +29,20 @@ const setupWrapper = (props?: Partial) => { ); }; -const proto = - 'syntax = "proto3";\npackage com.provectus;\n\nmessage TestProtoRecord {\n string f1 = 1;\n int32 f2 = 2;\n}\n'; - global.TextEncoder = TextEncoder; -const searchParamsContentAVRO = new URLSearchParams({ - keySerde: 'SchemaRegistry', - valueSerde: 'AVRO', - limit: '100', -}); - -const searchParamsContentJSON = new URLSearchParams({ - keySerde: 'SchemaRegistry', - valueSerde: 'JSON', - limit: '100', -}); - -const searchParamsContentPROTOBUF = new URLSearchParams({ - keySerde: 'SchemaRegistry', - valueSerde: 'PROTOBUF', - limit: '100', -}); describe('MessageContent screen', () => { beforeEach(() => { - render(setupWrapper(), { - initialEntries: [`/messages?${searchParamsContentAVRO}`], - }); + render(setupWrapper()); }); - describe('renders', () => { - it('key format in document', () => { + describe('Checking keySerde and valueSerde', () => { + it('keySerde in document', () => { expect(screen.getByText('SchemaRegistry')).toBeInTheDocument(); }); - it('content format in document', () => { - expect(screen.getByText('AVRO')).toBeInTheDocument(); + it('valueSerde in document', () => { + expect(screen.getByText('Avro')).toBeInTheDocument(); }); }); @@ -98,42 +78,3 @@ describe('MessageContent screen', () => { }); }); }); - -describe('checking content type depend on message type', () => { - it('renders component with message having JSON type', () => { - render( - setupWrapper({ - messageContent: '{"data": "test"}', - }), - { initialEntries: [`/messages?${searchParamsContentJSON}`] } - ); - expect(screen.getByText('JSON')).toBeInTheDocument(); - }); - it('renders component with message having AVRO type', () => { - render( - setupWrapper({ - messageContent: '{"data": "test"}', - }), - { initialEntries: [`/messages?${searchParamsContentAVRO}`] } - ); - expect(screen.getByText('AVRO')).toBeInTheDocument(); - }); - it('renders component with message having PROTOBUF type', () => { - render( - setupWrapper({ - messageContent: proto, - }), - { initialEntries: [`/messages?${searchParamsContentPROTOBUF}`] } - ); - expect(screen.getByText('PROTOBUF')).toBeInTheDocument(); - }); - it('renders component with message having no type which is equal to having PROTOBUF type', () => { - render( - setupWrapper({ - messageContent: '', - }), - { initialEntries: [`/messages?${searchParamsContentPROTOBUF}`] } - ); - expect(screen.getByText('PROTOBUF')).toBeInTheDocument(); - }); -}); diff --git a/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.styled.tsx b/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.styled.tsx index 483c41d05337c853735557fb39367113f090e4aa..d2750abf7d1152a5144a5bfc92c6dfb50d8c8c66 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.styled.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.styled.tsx @@ -8,15 +8,29 @@ export const Wrapper = styled.div` export const Columns = styled.div` margin: -0.75rem; margin-bottom: 0.75rem; + display: flex; + flex-direction: column; + padding: 0.75rem; + gap: 8px; @media screen and (min-width: 769px) { display: flex; } `; - -export const Column = styled.div` - flex-basis: 0; - flex-grow: 1; - flex-shrink: 1; - padding: 0.75rem; +export const Flex = styled.div` + display: flex; + flex-direction: row; + gap: 8px; + @media screen and (max-width: 1200px) { + flex-direction: column; + } +`; +export const FlexItem = styled.div` + width: 18rem; + @media screen and (max-width: 1450px) { + width: 50%; + } + @media screen and (max-width: 1200px) { + width: 100%; + } `; diff --git a/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.tsx b/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.tsx index 9450e512ad1030c46bafa21acec126bd1503039f..bacfa76c93faf8a71d7f9c0125b446b0c134b0ca 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.tsx @@ -4,6 +4,7 @@ import { RouteParamsClusterTopic } from 'lib/paths'; import { Button } from 'components/common/Button/Button'; import Editor from 'components/common/Editor/Editor'; import Select, { SelectOption } from 'components/common/Select/Select'; +import Switch from 'components/common/Switch/Switch'; import useAppParams from 'lib/hooks/useAppParams'; import { showAlert } from 'lib/errorHandling'; import { useSendMessage, useTopicDetails } from 'lib/hooks/api/topics'; @@ -26,9 +27,12 @@ interface FormType { partition: number; keySerde: string; valueSerde: string; + keepContents: boolean; } -const SendMessage: React.FC<{ onSubmit: () => void }> = ({ onSubmit }) => { +const SendMessage: React.FC<{ closeSidebar: () => void }> = ({ + closeSidebar, +}) => { const { clusterName, topicName } = useAppParams(); const { data: topic } = useTopicDetails({ clusterName, topicName }); const { data: serdes = {} } = useSerdes({ @@ -47,11 +51,13 @@ const SendMessage: React.FC<{ onSubmit: () => void }> = ({ onSubmit }) => { handleSubmit, formState: { isSubmitting }, control, + setValue, } = useForm({ mode: 'onChange', defaultValues: { ...defaultValues, partition: Number(partitionOptions[0].value), + keepContents: false, }, }); @@ -62,6 +68,7 @@ const SendMessage: React.FC<{ onSubmit: () => void }> = ({ onSubmit }) => { content, headers, partition, + keepContents, }: FormType) => { let errors: string[] = []; @@ -110,7 +117,11 @@ const SendMessage: React.FC<{ onSubmit: () => void }> = ({ onSubmit }) => { keySerde, valueSerde, }); - onSubmit(); + if (!keepContents) { + setValue('key', ''); + setValue('content', ''); + closeSidebar(); + } } catch (e) { // do nothing } @@ -120,7 +131,7 @@ const SendMessage: React.FC<{ onSubmit: () => void }> = ({ onSubmit }) => {
- + Partition void }> = ({ onSubmit }) => { /> )} /> - - - Key Serde + + + + Key Serde + ( + + )} + /> + + +
( - - )} - /> - + Keep contents +
- - +
Key void }> = ({ onSubmit }) => { /> )} /> - - +
+
Value void }> = ({ onSubmit }) => { /> )} /> - +
- +
Headers void }> = ({ onSubmit }) => { /> )} /> - +