diff --git a/package.json b/package.json index ce2886a365..6400f0e4be 100644 --- a/package.json +++ b/package.json @@ -236,6 +236,7 @@ "@elastic/eui": "34.6.0", "@reduxjs/toolkit": "^1.6.2", "@stablelib/snappy": "^1.0.2", + "@types/json-dup-key-validator": "^1.0.2", "ajv": "^8.17.1", "axios": "^1.8.4", "brotli-dec-wasm": "^2.3.0", @@ -259,6 +260,7 @@ "java-object-serialization": "^0.1.2", "js-yaml": "^4.1.0", "json-bigint": "^1.0.0", + "json-dup-key-validator": "^1.0.3", "jsonpath": "^1.1.1", "jszip": "^3.10.1", "lodash": "^4.17.21", diff --git a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/components/edit-entire-item-action/EditEntireItemAction.spec.tsx b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/components/edit-entire-item-action/EditEntireItemAction.spec.tsx index 0d889de7cd..46f669b8ca 100644 --- a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/components/edit-entire-item-action/EditEntireItemAction.spec.tsx +++ b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/components/edit-entire-item-action/EditEntireItemAction.spec.tsx @@ -51,4 +51,32 @@ describe('EditEntireItemAction', () => { ) expect(handleUpdateValueFormSubmit).not.toHaveBeenCalled() }) + + it('should show confirmation modal when JSON has duplicate keys, and confirm submit on confirm', () => { + const handleUpdateValueFormSubmit = jest.fn() + + const duplicateKeyJson = ` + { + "test": "one", + "test": "two" + } + ` + + render( + , + ) + + fireEvent.submit(screen.getByTestId('json-entire-form')) + + expect(screen.getByText(/Duplicate JSON key detected/i)).toBeInTheDocument() + + const confirmButton = screen.getByLabelText(/overwrite/i) + fireEvent.click(confirmButton) + + expect(handleUpdateValueFormSubmit).toHaveBeenCalledWith(duplicateKeyJson) + }) }) diff --git a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/components/edit-entire-item-action/EditEntireItemAction.tsx b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/components/edit-entire-item-action/EditEntireItemAction.tsx index bbd2924870..6ef6b5b3e9 100644 --- a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/components/edit-entire-item-action/EditEntireItemAction.tsx +++ b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/components/edit-entire-item-action/EditEntireItemAction.tsx @@ -1,11 +1,7 @@ import React, { ChangeEvent, useState } from 'react' -import { - EuiButtonIcon, - EuiForm, - EuiTextArea, - keys, -} from '@elastic/eui' +import { EuiButtonIcon, EuiForm, EuiTextArea, keys } from '@elastic/eui' import cx from 'classnames' +import jsonValidator from 'json-dup-key-validator' import FieldMessage from 'uiSrc/components/field-message/FieldMessage' import { Nullable } from 'uiSrc/utils' @@ -17,6 +13,7 @@ import { isValidJSON } from '../../utils' import { JSONErrors } from '../../constants' import styles from '../../styles.module.scss' +import ConfirmOverwrite from '../add-item/ConfirmOverwrite' export interface Props { initialValue: string @@ -28,6 +25,8 @@ const EditEntireItemAction = (props: Props) => { const { initialValue, onCancel, onSubmit } = props const [value, setValue] = useState(initialValue) const [error, setError] = useState>(null) + const [isConfirmationVisible, setIsConfirmationVisible] = + useState(false) const handleOnEsc = (e: KeyboardEvent) => { if (e.code?.toLowerCase() === keys.ESCAPE) { @@ -44,6 +43,17 @@ const EditEntireItemAction = (props: Props) => { return } + const validationError = jsonValidator.validate(value, false) + + if (validationError) { + setIsConfirmationVisible(true) + return + } + + onSubmit(value) + } + + const confirmApply = () => { onSubmit(value) } @@ -73,26 +83,32 @@ const EditEntireItemAction = (props: Props) => { data-testid="json-value" /> -
- - -
+ setIsConfirmationVisible(false)} + onConfirm={confirmApply} + > +
+ + +
+
{error && (