Repository: provectus/kafka-ui
Issue: Save to file issues with bigdecimal
Labels: type/bug
good first issue
scope/frontend
status/accepted

<!--

Don't forget to check for existing issues/discussions regarding your proposal. We might already have it.
https://github.com/provectus/kafka-ui/issues
https://github.com/provectus/kafka-ui/discussions

-->

**Describe the bug**
<!--(A clear and concise description of what the bug is.)-->

when payload contains bigdecimal values, values are displayed as unicode characters like, 

"foreignExchangeRate": {
			"bytes": "#òoÁ\u0000\u0000"
		}

When we select "save as a file", the result in file is truncated after bytes string as no text after this string in output file. ,\"foreignExchangeRate\":{\"bytes\":\"

**Set up**
<!--
How do you run the app? Please provide as much info as possible:
1. App version (docker image version or check commit hash in the top left corner in UI)
2. Helm chart version, if you use one
3. Any IAAC configs

We might close the issue without further explanation if you don't provide such information.
-->
d149d26013a81e22e5d0c8b59db56ac0565b9e55
https://github.com/provectus/kafka-ui/commit/d149d26

**Steps to Reproduce**
<!-- We'd like you to provide an example setup (via docker-compose, helm, etc.) 
to reproduce the problem, especially with a complex setups. -->
Steps to reproduce the behavior:

1. 

**Expected behavior**
<!--
(A clear and concise description of what you expected to happen)
-->

expected value to be displayed as  "foreignExchangeRate": 1.0000000000000000 instead of "foreignExchangeRate": {
			"bytes": "#òoÁ\u0000\u0000"
		} and payload should be downloaded as complete.

Note: with latest tag (v0.4.0), even the values are displayed as multi-byte, but save as a file copies the complete payload to file.

**Screenshots**
<!--
(If applicable, add screenshots to help explain your problem)
-->


**Additional context**
<!--
(Add any other context about the problem here)
-->


Issue comments:
Hello @nitin1677 , thank you for issue.

can you please share `foreignExchangeRate` field definition (like you have in avro schema) ?

{
						"name": "foreignExchangeRate",
						"type": [
							"null",
							{
								"type": "bytes",
								"logicalType": "decimal",
								"precision": 24,
								"scale": 16
							}
						],
						"doc": "foreign exchange rate",
						"default": null
					}
@nitin1677 , thank you for issue. 

There are two parts:
1. BE: currently kafka-ui doesnt support logical coversions (I created ticket for that, since it requires some time to implement https://github.com/provectus/kafka-ui/issues/2764) 
2. FE: I guess there is some error with downloading `#�òoÁ\u0000\u0000` symbols - lets check and fix that anyway

cc @Haarolean 
@iliax can we get an example schema/topic on our dev env for our frontend engineers to reproduce it easily?
@Haarolean I created topic `issue-2669` and submitted message that reproduces this issue
The issue has been closed but still this issue exists from latest pull of master.

		"field1": {
			"bytes": "#òoÁ\\u0000\\u0000"
		},

		"field2": {
			"bytes": "#òoÁ\\u0000\\u0000"
		},

		"field3": {
			"com.avro.schema.common.BigAmount_30_10": {
				"amount": "\\u0013Ôg\\u0000",
				"currency": "USD"
			}
		},


@nitin1677 which commit are you on? left top corner in ui
@nitin1677 please note, that within this issue we only fixed file exportion. Logical type support is not implemented yet (https://github.com/provectus/kafka-ui/issues/2764)

Pull request review comments:

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/lib/hooks/__tests__/useDataSaver.spec.tsx b/kafka-ui-react-app/src/lib/hooks/__tests__/useDataSaver.spec.tsx
index 510fb955455..f0631850e5c 100644
--- a/kafka-ui-react-app/src/lib/hooks/__tests__/useDataSaver.spec.tsx
+++ b/kafka-ui-react-app/src/lib/hooks/__tests__/useDataSaver.spec.tsx
@@ -15,30 +15,8 @@ describe('useDataSaver hook', () => {
 
     afterAll(() => jest.useRealTimers());
 
-    it('downloads json file', () => {
-      const link: HTMLAnchorElement = document.createElement('a');
-      link.click = jest.fn();
-
-      const mockCreate = jest
-        .spyOn(document, 'createElement')
-        .mockImplementation(() => link);
-
-      const HookWrapper: React.FC = () => {
-        const { saveFile } = useDataSaver('message', content);
-        useEffect(() => saveFile(), [saveFile]);
-        return null;
-      };
-
-      render(<HookWrapper />);
-      expect(mockCreate).toHaveBeenCalledTimes(2);
-      expect(link.download).toEqual('message_1616581196000.json');
-      expect(link.href).toEqual(`data:text/json;charset=utf-8,${content}`);
-      expect(link.click).toHaveBeenCalledTimes(1);
-
-      mockCreate.mockRestore();
-    });
-
     it('downloads txt file', () => {
+      global.URL.createObjectURL = jest.fn();
       const link: HTMLAnchorElement = document.createElement('a');
       link.click = jest.fn();
 
@@ -54,8 +32,7 @@ describe('useDataSaver hook', () => {
 
       render(<HookWrapper />);
       expect(mockCreate).toHaveBeenCalledTimes(2);
-      expect(link.download).toEqual('message_1616581196000.txt');
-      expect(link.href).toEqual(`data:text/json;charset=utf-8,content`);
+      expect(link.download).toEqual('message');
       expect(link.click).toHaveBeenCalledTimes(1);
 
       mockCreate.mockRestore();
diff --git a/kafka-ui-react-app/src/lib/hooks/useDataSaver.ts b/kafka-ui-react-app/src/lib/hooks/useDataSaver.ts
index 6dba2700be8..d1ff61386dc 100644
--- a/kafka-ui-react-app/src/lib/hooks/useDataSaver.ts
+++ b/kafka-ui-react-app/src/lib/hooks/useDataSaver.ts
@@ -1,4 +1,3 @@
-import isObject from 'lodash/isObject';
 import { showSuccessAlert } from 'lib/errorHandling';
 
 const useDataSaver = (
@@ -17,19 +16,14 @@ const useDataSaver = (
       });
     }
   };
-
   const saveFile = () => {
-    const extension = isObject(data) ? 'json' : 'txt';
-    const dataStr = `data:text/json;charset=utf-8,${data}`;
-    const downloadAnchorNode = document.createElement('a');
-    downloadAnchorNode.setAttribute('href', dataStr);
-    downloadAnchorNode.setAttribute(
-      'download',
-      `${subject}_${new Date().getTime()}.${extension}`
-    );
-    document.body.appendChild(downloadAnchorNode);
-    downloadAnchorNode.click();
-    downloadAnchorNode.remove();
+    const blob = new Blob([data as BlobPart], { type: 'text/json' });
+    const elem = window.document.createElement('a');
+    elem.href = window.URL.createObjectURL(blob);
+    elem.download = subject;
+    document.body.appendChild(elem);
+    elem.click();
+    document.body.removeChild(elem);
   };
 
   return { copyToClipboard, saveFile };


Test patch: