diff --git a/api/src/main/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerde.java b/api/src/main/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerde.java index d6f7a3699..76a5a1d45 100644 --- a/api/src/main/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerde.java +++ b/api/src/main/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerde.java @@ -282,13 +282,16 @@ public Deserializer deserializer(String topic, Target type) { var schemaId = extractSchemaIdFromMsg(data); SchemaType format = getMessageFormatBySchemaId(schemaId); MessageFormatter formatter = schemaRegistryFormatters.get(format); + Map additionalProperties = new HashMap<>(Map.of( + "schemaId", schemaId, + "type", format.name() + )); + getSchemaById(schemaId).map(ParsedSchema::name) + .ifPresent(name -> additionalProperties.put("name", name)); return new DeserializeResult( formatter.format(topic, data), DeserializeResult.Type.JSON, - Map.of( - "schemaId", schemaId, - "type", format.name() - ) + additionalProperties ); }; } diff --git a/api/src/test/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerdeTest.java b/api/src/test/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerdeTest.java index d66a8d004..94c60109a 100644 --- a/api/src/test/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerdeTest.java +++ b/api/src/test/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerdeTest.java @@ -127,7 +127,8 @@ void deserializeReturnsJsonAvroMsgJsonRepresentation() throws RestClientExceptio assertThat(result.getType()).isEqualTo(DeserializeResult.Type.JSON); assertThat(result.getAdditionalProperties()) .contains(Map.entry("type", "AVRO")) - .contains(Map.entry("schemaId", schemaId)); + .contains(Map.entry("schemaId", schemaId)) + .contains(Map.entry("name", "TestAvroRecord1")); } @Nested diff --git a/frontend/src/components/Topics/Topic/Messages/Message.tsx b/frontend/src/components/Topics/Topic/Messages/Message.tsx index af76db673..e6d7aff35 100644 --- a/frontend/src/components/Topics/Topic/Messages/Message.tsx +++ b/frontend/src/components/Topics/Topic/Messages/Message.tsx @@ -37,6 +37,8 @@ const Message: React.FC = ({ headers, valueSerde, keySerde, + keyDeserializeProperties, + valueDeserializeProperties, }, keyFilters, contentFilters, @@ -157,6 +159,8 @@ const Message: React.FC = ({ contentSize={valueSize} keySerde={keySerde} valueSerde={valueSerde} + keyDeserializeProperties={keyDeserializeProperties} + valueDeserializeProperties={valueDeserializeProperties} /> )} diff --git a/frontend/src/components/Topics/Topic/Messages/MessageContent/AvroMetadata.tsx b/frontend/src/components/Topics/Topic/Messages/MessageContent/AvroMetadata.tsx new file mode 100644 index 000000000..c14dc65ab --- /dev/null +++ b/frontend/src/components/Topics/Topic/Messages/MessageContent/AvroMetadata.tsx @@ -0,0 +1,43 @@ +import React from 'react'; + +import * as S from './MessageContent.styled'; + +export interface AvroMetadataProps { + deserializeProperties?: { [key: string]: unknown | undefined }; +} + +const AvroMetadata: React.FC = ({ + deserializeProperties, +}) => { + if ( + !deserializeProperties || + deserializeProperties.type !== 'AVRO' || + !deserializeProperties.name || + !deserializeProperties.schemaId + ) { + return null; + } + + if ( + typeof deserializeProperties.name !== 'string' || + typeof deserializeProperties.schemaId !== 'number' + ) { + return null; + } + + return ( + + Value Type + + + {deserializeProperties.name.split('.').pop()} + + + Schema Id: {deserializeProperties.schemaId} + + + + ); +}; + +export default AvroMetadata; diff --git a/frontend/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx b/frontend/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx index ad35399a9..b0f5e1af6 100644 --- a/frontend/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx +++ b/frontend/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx @@ -4,6 +4,7 @@ import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted'; import { SchemaType, TopicMessageTimestampTypeEnum } from 'generated-sources'; import { formatTimestamp } from 'lib/dateTimeHelpers'; +import AvroMetadata from './AvroMetadata'; import * as S from './MessageContent.styled'; type Tab = 'key' | 'content' | 'headers'; @@ -18,6 +19,8 @@ export interface MessageContentProps { contentSize?: number; keySerde?: string; valueSerde?: string; + keyDeserializeProperties?: { [key: string]: unknown | undefined }; + valueDeserializeProperties?: { [key: string]: unknown | undefined }; } const MessageContent: React.FC = ({ @@ -30,6 +33,8 @@ const MessageContent: React.FC = ({ contentSize, keySerde, valueSerde, + keyDeserializeProperties, + valueDeserializeProperties, }) => { const [activeTab, setActiveTab] = React.useState('content'); const activeTabContent = () => { @@ -118,6 +123,8 @@ const MessageContent: React.FC = ({ + + Value Serde @@ -127,6 +134,8 @@ const MessageContent: React.FC = ({ + + diff --git a/frontend/src/components/Topics/Topic/Messages/MessageContent/__tests__/AvroMetadata.spec.tsx b/frontend/src/components/Topics/Topic/Messages/MessageContent/__tests__/AvroMetadata.spec.tsx new file mode 100644 index 000000000..2e0a3dca7 --- /dev/null +++ b/frontend/src/components/Topics/Topic/Messages/MessageContent/__tests__/AvroMetadata.spec.tsx @@ -0,0 +1,43 @@ +import { TextEncoder } from 'util'; + +import React from 'react'; +import { screen } from '@testing-library/react'; +import { render } from 'lib/testHelpers'; +import AvroMetadata, { + AvroMetadataProps, +} from 'components/Topics/Topic/Messages/MessageContent/AvroMetadata'; + +const setupWrapper = (props?: Partial) => { + return ( + + + + +
+ ); +}; + +global.TextEncoder = TextEncoder; + +describe('AvroMetadata screen', () => { + beforeEach(() => { + render(setupWrapper()); + }); + + describe('Checking type and schema id', () => { + it('type in document', () => { + expect(screen.getByText('MessageType')).toBeInTheDocument(); + }); + + it('schema id in document', () => { + expect(screen.getByText('Schema Id: 1')).toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx b/frontend/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx index d76455242..73b667db6 100644 --- a/frontend/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx +++ b/frontend/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx @@ -22,6 +22,11 @@ const setupWrapper = (props?: Partial) => { timestampType={TopicMessageTimestampTypeEnum.CREATE_TIME} keySerde="SchemaRegistry" valueSerde="Avro" + valueDeserializeProperties={{ + type: 'AVRO', + name: 'MessageType', + schemaId: 1, + }} {...props} /> @@ -36,7 +41,7 @@ describe('MessageContent screen', () => { render(setupWrapper()); }); - describe('Checking keySerde and valueSerde', () => { + describe('Checking keySerde, valueSerde and valueType', () => { it('keySerde in document', () => { expect(screen.getByText('SchemaRegistry')).toBeInTheDocument(); }); @@ -44,6 +49,10 @@ describe('MessageContent screen', () => { it('valueSerde in document', () => { expect(screen.getByText('Avro')).toBeInTheDocument(); }); + + it('valueType in document', () => { + expect(screen.getByText('Value Type')).toBeInTheDocument(); + }); }); describe('when switched to display the key', () => {