Message.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import React from 'react';
  2. import useDataSaver from 'lib/hooks/useDataSaver';
  3. import { TopicMessage } from 'generated-sources';
  4. import MessageToggleIcon from 'components/common/Icons/MessageToggleIcon';
  5. import IconButtonWrapper from 'components/common/Icons/IconButtonWrapper';
  6. import { Dropdown, DropdownItem } from 'components/common/Dropdown';
  7. import { formatTimestamp } from 'lib/dateTimeHelpers';
  8. import { JSONPath } from 'jsonpath-plus';
  9. import Ellipsis from 'components/common/Ellipsis/Ellipsis';
  10. import WarningRedIcon from 'components/common/Icons/WarningRedIcon';
  11. import MessageContent from './MessageContent/MessageContent';
  12. import * as S from './MessageContent/MessageContent.styled';
  13. export interface PreviewFilter {
  14. field: string;
  15. path: string;
  16. }
  17. export interface Props {
  18. keyFilters: PreviewFilter[];
  19. contentFilters: PreviewFilter[];
  20. message: TopicMessage;
  21. }
  22. const Message: React.FC<Props> = ({
  23. message: {
  24. timestamp,
  25. timestampType,
  26. offset,
  27. key,
  28. partition,
  29. content,
  30. headers,
  31. valueSerde,
  32. keySerde,
  33. },
  34. keyFilters,
  35. contentFilters,
  36. }) => {
  37. const [isOpen, setIsOpen] = React.useState(false);
  38. const savedMessageJson = {
  39. Value: content,
  40. Offset: offset,
  41. Key: key,
  42. Partition: partition,
  43. Headers: headers,
  44. Timestamp: timestamp,
  45. };
  46. const savedMessage = JSON.stringify(savedMessageJson, null, '\t');
  47. const { copyToClipboard, saveFile } = useDataSaver(
  48. 'topic-message',
  49. savedMessage || ''
  50. );
  51. const toggleIsOpen = () => setIsOpen(!isOpen);
  52. const [vEllipsisOpen, setVEllipsisOpen] = React.useState(false);
  53. const getParsedJson = (jsonValue: string) => {
  54. try {
  55. return JSON.parse(jsonValue);
  56. } catch (e) {
  57. return {};
  58. }
  59. };
  60. const renderFilteredJson = (
  61. jsonValue?: string,
  62. filters?: PreviewFilter[]
  63. ) => {
  64. if (!filters?.length || !jsonValue) return jsonValue;
  65. const parsedJson = getParsedJson(jsonValue);
  66. return (
  67. <>
  68. {filters.map((item) => {
  69. return (
  70. <div key={`${item.path}--${item.field}`}>
  71. {item.field}:{' '}
  72. {JSON.stringify(
  73. JSONPath({ path: item.path, json: parsedJson, wrap: false })
  74. )}
  75. </div>
  76. );
  77. })}
  78. </>
  79. );
  80. };
  81. return (
  82. <>
  83. <S.ClickableRow
  84. onMouseEnter={() => setVEllipsisOpen(true)}
  85. onMouseLeave={() => setVEllipsisOpen(false)}
  86. onClick={toggleIsOpen}
  87. >
  88. <td>
  89. <IconButtonWrapper aria-hidden>
  90. <MessageToggleIcon isOpen={isOpen} />
  91. </IconButtonWrapper>
  92. </td>
  93. <td>{offset}</td>
  94. <td>{partition}</td>
  95. <td>
  96. <div>{formatTimestamp(timestamp)}</div>
  97. </td>
  98. <S.DataCell title={key}>
  99. <Ellipsis text={renderFilteredJson(key, keyFilters)}>
  100. {keySerde === 'Fallback' && <WarningRedIcon />}
  101. </Ellipsis>
  102. </S.DataCell>
  103. <S.DataCell title={content}>
  104. <S.Metadata>
  105. <S.MetadataValue>
  106. <Ellipsis text={renderFilteredJson(content, contentFilters)}>
  107. {valueSerde === 'Fallback' && <WarningRedIcon />}
  108. </Ellipsis>
  109. </S.MetadataValue>
  110. </S.Metadata>
  111. </S.DataCell>
  112. <td style={{ width: '5%' }}>
  113. {vEllipsisOpen && (
  114. <Dropdown>
  115. <DropdownItem onClick={copyToClipboard}>
  116. Copy to clipboard
  117. </DropdownItem>
  118. <DropdownItem onClick={saveFile}>Save as a file</DropdownItem>
  119. </Dropdown>
  120. )}
  121. </td>
  122. </S.ClickableRow>
  123. {isOpen && (
  124. <MessageContent
  125. messageKey={key}
  126. messageContent={content}
  127. headers={headers}
  128. timestamp={timestamp}
  129. timestampType={timestampType}
  130. />
  131. )}
  132. </>
  133. );
  134. };
  135. export default Message;