diff --git a/package.json b/package.json index 68e5805374..5901dc8958 100644 --- a/package.json +++ b/package.json @@ -206,6 +206,7 @@ "dependencies": { "@elastic/datemath": "^5.0.3", "@elastic/eui": "34.6.0", + "@msgpack/msgpack": "^2.7.2", "@reduxjs/toolkit": "^1.6.2", "axios": "^0.25.0", "classnames": "^2.3.1", diff --git a/redisinsight/ui/src/pages/browser/components/hash-details/HashDetails.tsx b/redisinsight/ui/src/pages/browser/components/hash-details/HashDetails.tsx index d602db8bbd..981da2c806 100644 --- a/redisinsight/ui/src/pages/browser/components/hash-details/HashDetails.tsx +++ b/redisinsight/ui/src/pages/browser/components/hash-details/HashDetails.tsx @@ -23,7 +23,8 @@ import { bufferToString, isEqualBuffers, isTextViewFormatter, - getSerializedFormat + bufferToSerializedFormat, + stringToSerializedBufferFormat } from 'uiSrc/utils' import { sendEventTelemetry, TelemetryEvent, getBasedOnViewTypeEvent, getMatchType } from 'uiSrc/telemetry' import VirtualTable from 'uiSrc/components/virtual-table/VirtualTable' @@ -149,7 +150,7 @@ const HashDetails = (props: Props) => { const handleEditField = useCallback((field = '', editing: boolean) => { setFields((prevFields) => prevFields.map((item) => { if (isEqualBuffers(item.field, field)) { - const value = getSerializedFormat(viewFormat, bufferToString(item.value), 4) + const value = bufferToSerializedFormat(viewFormat, item.value, 4) setAreaValue(value) return { ...item, editing } } @@ -165,7 +166,7 @@ const HashDetails = (props: Props) => { const handleApplyEditField = (field = '') => { const data: AddFieldsToHashDto = { keyName: key, - fields: [{ field, value: stringToBuffer(getSerializedFormat(viewFormat, areaValue)) }], + fields: [{ field, value: stringToSerializedBufferFormat(viewFormat, areaValue) }], } dispatch(updateHashFieldsAction(data, () => onHashEditedSuccess(field))) } diff --git a/redisinsight/ui/src/pages/browser/components/key-details-header/components/Formatter/constants.ts b/redisinsight/ui/src/pages/browser/components/key-details-header/components/Formatter/constants.ts index 2056618989..cfec5cbb11 100644 --- a/redisinsight/ui/src/pages/browser/components/key-details-header/components/Formatter/constants.ts +++ b/redisinsight/ui/src/pages/browser/components/key-details-header/components/Formatter/constants.ts @@ -15,6 +15,12 @@ export const KEY_VALUE_FORMATTER_OPTIONS = [ iconLight: 'kqlSelector', value: KeyValueFormat.JSON, }, + { + text: 'Msgpack', + iconDark: 'kqlSelector', + iconLight: 'kqlSelector', + value: KeyValueFormat.Msgpack, + }, ] export const KEY_VALUE_JSON_FORMATTER_OPTIONS = [KeyValueFormat.Unicode] diff --git a/redisinsight/ui/src/pages/browser/components/list-details/ListDetails.tsx b/redisinsight/ui/src/pages/browser/components/list-details/ListDetails.tsx index 378ed2a488..8cc1814c1d 100644 --- a/redisinsight/ui/src/pages/browser/components/list-details/ListDetails.tsx +++ b/redisinsight/ui/src/pages/browser/components/list-details/ListDetails.tsx @@ -23,11 +23,12 @@ import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances' import { sendEventTelemetry, TelemetryEvent, getBasedOnViewTypeEvent } from 'uiSrc/telemetry' import { KeyTypes, OVER_RENDER_BUFFER_COUNT, TableCellAlignment } from 'uiSrc/constants' import { + bufferToSerializedFormat, bufferToString, formatLongName, formattingBuffer, - getSerializedFormat, isTextViewFormatter, + stringToSerializedBufferFormat, validateListIndex } from 'uiSrc/utils' import { selectedKeyDataSelector, keysSelector, selectedKeySelector } from 'uiSrc/slices/browser/keys' @@ -36,7 +37,6 @@ import VirtualTable from 'uiSrc/components/virtual-table/VirtualTable' import InlineItemEditor from 'uiSrc/components/inline-item-editor/InlineItemEditor' import { StopPropagation } from 'uiSrc/components/virtual-table' import { getColumnWidth } from 'uiSrc/components/virtual-grid' -import { stringToBuffer } from 'uiSrc/utils/formatters/bufferFormatters' import { SetListElementDto, SetListElementResponse, @@ -115,7 +115,7 @@ const ListDetails = (props: Props) => { const handleEditElement = (index = 0, editing: boolean) => { const newElemsState = elements.map((item) => { if (item.index === index) { - const value = getSerializedFormat(viewFormat, bufferToString(item.element), 4) + const value = bufferToSerializedFormat(viewFormat, item.element, 4) setAreaValue(value) return { ...item, editing } } @@ -135,7 +135,7 @@ const ListDetails = (props: Props) => { const handleApplyEditElement = (index = 0) => { const data: SetListElementDto = { keyName: key, - element: stringToBuffer(getSerializedFormat(viewFormat, areaValue)), + element: stringToSerializedBufferFormat(viewFormat, areaValue), index, } dispatch( diff --git a/redisinsight/ui/src/pages/browser/components/string-details/StringDetails.tsx b/redisinsight/ui/src/pages/browser/components/string-details/StringDetails.tsx index 1db8dce3aa..80f95d6e80 100644 --- a/redisinsight/ui/src/pages/browser/components/string-details/StringDetails.tsx +++ b/redisinsight/ui/src/pages/browser/components/string-details/StringDetails.tsx @@ -10,7 +10,13 @@ import { useDispatch, useSelector } from 'react-redux' import { EuiProgress, EuiText, EuiTextArea, EuiToolTip } from '@elastic/eui' import { RedisResponseBuffer } from 'uiSrc/slices/interfaces' -import { bufferToString, formattingBuffer, getSerializedFormat, isTextViewFormatter } from 'uiSrc/utils' +import { + bufferToSerializedFormat, + bufferToString, + formattingBuffer, + isTextViewFormatter, + stringToSerializedBufferFormat +} from 'uiSrc/utils' import { resetStringValue, stringDataSelector, @@ -93,12 +99,12 @@ const StringDetails = (props: Props) => { useMemo(() => { if (isEditItem) { (document.activeElement as HTMLElement)?.blur() - setAreaValue(getSerializedFormat(viewFormat, bufferToString(value), 4)) + setAreaValue(bufferToSerializedFormat(viewFormat, value, 4)) } }, [isEditItem]) const onApplyChanges = () => { - const data = stringToBuffer(getSerializedFormat(viewFormat, areaValue)) + const data = stringToSerializedBufferFormat(viewFormat, areaValue) const onSuccess = () => { setIsEdit(false) setValue(data) @@ -107,7 +113,7 @@ const StringDetails = (props: Props) => { } const onDeclineChanges = () => { - setAreaValue(getSerializedFormat(viewFormat, bufferToString(value), 4)) + setAreaValue(bufferToSerializedFormat(viewFormat, value, 4)) setIsEdit(false) } diff --git a/redisinsight/ui/src/utils/formatters/valueFormatters.tsx b/redisinsight/ui/src/utils/formatters/valueFormatters.tsx index bee7ac2039..4ac1136727 100644 --- a/redisinsight/ui/src/utils/formatters/valueFormatters.tsx +++ b/redisinsight/ui/src/utils/formatters/valueFormatters.tsx @@ -1,7 +1,8 @@ +import { decode, encode } from '@msgpack/msgpack' import JSONViewer from 'uiSrc/components/json-viewer/JSONViewer' import { KeyValueFormat } from 'uiSrc/constants' import { RedisResponseBuffer } from 'uiSrc/slices/interfaces' -import { bufferToUTF8 } from 'uiSrc/utils' +import { anyToBuffer, bufferToUTF8, stringToBuffer, UintArrayToString } from 'uiSrc/utils' import { reSerializeJSON } from 'uiSrc/utils/formatters/json' interface FormattingProps { @@ -30,8 +31,13 @@ const formattingBuffer = ( return bufferToJSON(reply, props as FormattingProps) } case KeyValueFormat.Msgpack: { - // TODO: for future - return { value: bufferToUnicode(reply), isValid: true } + try { + const decoded = decode(reply.data) + const value = JSON.stringify(decoded) + return JSONViewer({ value, ...props }) + } catch (e) { + return { value: bufferToUTF8(reply), isValid: false } + } } default: { return { value: bufferToUnicode(reply), isValid: true } @@ -39,13 +45,42 @@ const formattingBuffer = ( } } -const getSerializedFormat = (format: KeyValueFormat, val: string, space?: number) => { +const bufferToSerializedFormat = (format: KeyValueFormat, value: RedisResponseBuffer, space?: number): string => { switch (format) { case KeyValueFormat.JSON: { - return reSerializeJSON(val, space) + return reSerializeJSON(bufferToUTF8(value), space) + } + case KeyValueFormat.Msgpack: { + try { + const decoded = decode(value.data) + const stringified = JSON.stringify(decoded) + return reSerializeJSON(stringified, space) + } catch (e) { + return bufferToUTF8(value) + } + } + default: { + return bufferToUTF8(value) + } + } +} + +const stringToSerializedBufferFormat = (format: KeyValueFormat, value: string): RedisResponseBuffer => { + switch (format) { + case KeyValueFormat.JSON: { + return stringToBuffer(reSerializeJSON(value)) + } + case KeyValueFormat.Msgpack: { + try { + const json = JSON.parse(value) + const encoded = encode(json) + return anyToBuffer(encoded) + } catch (e) { + return stringToBuffer(value) + } } default: { - return val + return stringToBuffer(value) } } } @@ -54,5 +89,6 @@ export { formattingBuffer, isTextViewFormatter, isJsonViewFormatter, - getSerializedFormat + bufferToSerializedFormat, + stringToSerializedBufferFormat } diff --git a/yarn.lock b/yarn.lock index 8f64e36039..e0e324ce13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1625,6 +1625,11 @@ dependencies: extend "3.0.2" +"@msgpack/msgpack@^2.7.2": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-2.7.2.tgz#f34b8aa0c49f0dd55eb7eba577081299cbf3f90b" + integrity sha512-rYEi46+gIzufyYUAoHDnRzkWGxajpD9vVXFQ3g1vbjrBm6P7MBmm+s/fqPa46sxa+8FOUdEuRQKaugo5a4JWpw== + "@nestjs/cli@^7.0.0": version "7.5.4" resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-7.5.4.tgz#d4cdce388d7f6a32dabdf5bab909af23653f7740"