Merge branch 'develop' into feat/opamp-logparing

This commit is contained in:
Vishal Sharma 2023-03-20 17:40:09 +05:30 committed by GitHub
commit 0fa20445d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 115 additions and 17 deletions

View File

@ -1,6 +1,6 @@
import MEditor, { EditorProps } from '@monaco-editor/react'; import MEditor, { EditorProps } from '@monaco-editor/react';
import { useIsDarkMode } from 'hooks/useDarkMode'; import { useIsDarkMode } from 'hooks/useDarkMode';
import React from 'react'; import React, { useMemo } from 'react';
function Editor({ function Editor({
value, value,
@ -11,16 +11,24 @@ function Editor({
options, options,
}: MEditorProps): JSX.Element { }: MEditorProps): JSX.Element {
const isDarkMode = useIsDarkMode(); const isDarkMode = useIsDarkMode();
const onChangeHandler = (newValue?: string): void => {
if (typeof newValue === 'string' && onChange) onChange(newValue);
};
const editorOptions = useMemo(
() => ({ fontSize: 16, automaticLayout: true, readOnly, ...options }),
[options, readOnly],
);
return ( return (
<MEditor <MEditor
theme={isDarkMode ? 'vs-dark' : 'vs-light'} theme={isDarkMode ? 'vs-dark' : 'vs-light'}
language={language} language={language}
value={value} value={value}
options={{ fontSize: 16, automaticLayout: true, readOnly, ...options }} options={editorOptions}
height={height} height={height}
onChange={(newValue): void => { onChange={onChangeHandler}
if (typeof newValue === 'string') onChange(newValue);
}}
/> />
); );
} }
@ -28,7 +36,7 @@ function Editor({
interface MEditorProps { interface MEditorProps {
value: string; value: string;
language?: string; language?: string;
onChange: (value: string) => void; onChange?: (value: string) => void;
readOnly?: boolean; readOnly?: boolean;
height?: string; height?: string;
options?: EditorProps['options']; options?: EditorProps['options'];
@ -39,6 +47,7 @@ Editor.defaultProps = {
readOnly: false, readOnly: false,
height: '40vh', height: '40vh',
options: {}, options: {},
onChange: (): void => {},
}; };
export default Editor; export default Editor;

View File

@ -1,14 +1,18 @@
import { blue, orange } from '@ant-design/colors'; import { blue, orange } from '@ant-design/colors';
import { Input } from 'antd'; import { Input } from 'antd';
import { ColumnsType } from 'antd/es/table';
import Editor from 'components/Editor';
import AddToQueryHOC from 'components/Logs/AddToQueryHOC'; import AddToQueryHOC from 'components/Logs/AddToQueryHOC';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC'; import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
import { ResizeTable } from 'components/ResizeTable'; import { ResizeTable } from 'components/ResizeTable';
import flatten from 'flat'; import flatten from 'flat';
import { fieldSearchFilter } from 'lib/logs/fieldSearch'; import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import { isEmpty } from 'lodash-es';
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import { ILog } from 'types/api/logs/log'; import { ILog } from 'types/api/logs/log';
import ActionItem from './ActionItem'; import ActionItem from './ActionItem';
import { recursiveParseJSON } from './utils';
// Fields which should be restricted from adding it to query // Fields which should be restricted from adding it to query
const RESTRICTED_FIELDS = ['timestamp']; const RESTRICTED_FIELDS = ['timestamp'];
@ -41,10 +45,10 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
return null; return null;
} }
const columns = [ const columns: ColumnsType<DataType> = [
{ {
title: 'Action', title: 'Action',
width: 100, width: 15,
render: (fieldData: Record<string, string>): JSX.Element | null => { render: (fieldData: Record<string, string>): JSX.Element | null => {
const fieldKey = fieldData.field.split('.').slice(-1); const fieldKey = fieldData.field.split('.').slice(-1);
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) { if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
@ -57,7 +61,8 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
title: 'Field', title: 'Field',
dataIndex: 'field', dataIndex: 'field',
key: 'field', key: 'field',
width: 100, width: 30,
ellipsis: true,
render: (field: string): JSX.Element => { render: (field: string): JSX.Element => {
const fieldKey = field.split('.').slice(-1); const fieldKey = field.split('.').slice(-1);
const renderedField = <span style={{ color: blue[4] }}>{field}</span>; const renderedField = <span style={{ color: blue[4] }}>{field}</span>;
@ -78,16 +83,36 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
key: 'value', key: 'value',
width: 80, width: 80,
ellipsis: false, ellipsis: false,
render: (field: never): JSX.Element => ( render: (field, record): JSX.Element => {
if (record.field === 'body') {
const parsedBody = recursiveParseJSON(field);
if (!isEmpty(parsedBody)) {
return (
<Editor
value={JSON.stringify(parsedBody, null, 2)}
readOnly
height="70vh"
options={{
minimap: {
enabled: false,
},
}}
/>
);
}
}
return (
<CopyClipboardHOC textToCopy={field}> <CopyClipboardHOC textToCopy={field}>
<span style={{ color: orange[6] }}>{field}</span> <span style={{ color: orange[6] }}>{field}</span>
</CopyClipboardHOC> </CopyClipboardHOC>
), );
},
}, },
]; ];
return ( return (
<div style={{ position: 'relative' }}> <>
<Input <Input
placeholder="Search field names" placeholder="Search field names"
size="large" size="large"
@ -95,13 +120,19 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
onChange={(e): void => setFieldSearchInput(e.target.value)} onChange={(e): void => setFieldSearchInput(e.target.value)}
/> />
<ResizeTable <ResizeTable
columns={columns as never} columns={columns}
tableLayout="fixed" tableLayout="fixed"
dataSource={dataSource} dataSource={dataSource}
pagination={false} pagination={false}
/> />
</div> </>
); );
} }
interface DataType {
key: string;
field: string;
value: string;
}
export default TableView; export default TableView;

View File

@ -0,0 +1,47 @@
import { recursiveParseJSON } from './utils';
describe('recursiveParseJSON', () => {
it('should return an empty object if the input is not valid JSON', () => {
const result = recursiveParseJSON('not valid JSON');
expect(result).toEqual({});
});
it('should return the parsed JSON object for valid JSON input', () => {
const jsonString = '{"name": "John", "age": 30}';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual({ name: 'John', age: 30 });
});
it('should recursively parse nested JSON objects', () => {
const jsonString =
'{"name": "John", "age": 30, "address": {"street": "123 Main St", "city": "Anytown", "state": "CA"}}';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual({
name: 'John',
age: 30,
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA',
},
});
});
it('should recursively parse nested JSON arrays', () => {
const jsonString = '[1, 2, [3, 4], {"foo": "bar"}]';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual([1, 2, [3, 4], { foo: 'bar' }]);
});
it('should recursively parse deeply nested JSON objects', () => {
const jsonString = '{"foo": {"bar": {"baz": {"qux": {"value": 42}}}}}';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual({ foo: { bar: { baz: { qux: { value: 42 } } } } });
});
it('should handle JSON input that contains escaped characters', () => {
const jsonString = '{"name": "John\\", \\"Doe", "age": 30}';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual({ name: 'John", "Doe', age: 30 });
});
});

View File

@ -0,0 +1,11 @@
export const recursiveParseJSON = (obj: string): Record<string, unknown> => {
try {
const value = JSON.parse(obj);
if (typeof value === 'string') {
return recursiveParseJSON(value);
}
return value;
} catch (e) {
return {};
}
};