This is an automated email from the ASF dual-hosted git repository.
vogievetsky pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 1873fca6c7 Web console: update DQT to latest version and fix bigint
crash (#14318)
1873fca6c7 is described below
commit 1873fca6c73f816db45482eda3e59abc6fd4e8f5
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Wed May 24 17:40:45 2023 -0700
Web console: update DQT to latest version and fix bigint crash (#14318)
* update dqt
* don't crash on bigint values
* better submit experiance
* bump to an even version
---
licenses.yaml | 2 +-
web-console/package-lock.json | 14 ++++-----
web-console/package.json | 2 +-
.../src/components/json-collapse/json-collapse.tsx | 2 +-
.../src/dialogs/spec-dialog/spec-dialog.tsx | 34 ++++++++++++++--------
web-console/src/utils/general.spec.ts | 16 ++++++++++
web-console/src/utils/general.tsx | 23 +++++++++++++++
web-console/src/utils/object-change.spec.ts | 2 +-
.../load-data-view/filter-table/filter-table.tsx | 8 ++---
.../parse-time-table/parse-time-table.tsx | 5 ++--
.../load-data-view/schema-table/schema-table.tsx | 5 ++--
.../transform-table/transform-table.tsx | 5 ++--
.../execution-submit-dialog.tsx | 22 ++++++++------
13 files changed, 98 insertions(+), 42 deletions(-)
diff --git a/licenses.yaml b/licenses.yaml
index 608633a01a..62f1991f76 100644
--- a/licenses.yaml
+++ b/licenses.yaml
@@ -5814,7 +5814,7 @@ license_category: binary
module: web-console
license_name: Apache License version 2.0
copyright: Imply Data
-version: 0.18.3
+version: 0.18.12
---
diff --git a/web-console/package-lock.json b/web-console/package-lock.json
index 119224bff1..10ba50cf2f 100644
--- a/web-console/package-lock.json
+++ b/web-console/package-lock.json
@@ -22,7 +22,7 @@
"d3-axis": "^2.1.0",
"d3-scale": "^3.3.0",
"d3-selection": "^2.0.0",
- "druid-query-toolkit": "^0.18.3",
+ "druid-query-toolkit": "^0.18.12",
"file-saver": "^2.0.2",
"follow-redirects": "^1.14.7",
"fontsource-open-sans": "^3.0.9",
@@ -8211,9 +8211,9 @@
}
},
"node_modules/druid-query-toolkit": {
- "version": "0.18.3",
- "resolved":
"https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.18.3.tgz",
- "integrity":
"sha512-Za2U2NsFyun5HXeWnLCICnTFzZp4aC17aSOjgVbQgEWZNMPht51U4paE3SVhPDObkWDjDUYAqVv+mO+ZyMx9Og==",
+ "version": "0.18.12",
+ "resolved":
"https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.18.12.tgz",
+ "integrity":
"sha512-wDcZUW8vhiJXARC44EFFwUeZW6lawXWv++bxHIUKaxq3M5byBuWPKjEDTCdPEHprxmR2sxaTpsPw4A6KiRmBog==",
"dependencies": {
"tslib": "^2.3.1"
},
@@ -32625,9 +32625,9 @@
}
},
"druid-query-toolkit": {
- "version": "0.18.3",
- "resolved":
"https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.18.3.tgz",
- "integrity":
"sha512-Za2U2NsFyun5HXeWnLCICnTFzZp4aC17aSOjgVbQgEWZNMPht51U4paE3SVhPDObkWDjDUYAqVv+mO+ZyMx9Og==",
+ "version": "0.18.12",
+ "resolved":
"https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.18.12.tgz",
+ "integrity":
"sha512-wDcZUW8vhiJXARC44EFFwUeZW6lawXWv++bxHIUKaxq3M5byBuWPKjEDTCdPEHprxmR2sxaTpsPw4A6KiRmBog==",
"requires": {
"tslib": "^2.3.1"
}
diff --git a/web-console/package.json b/web-console/package.json
index 68b83bfe99..9dba6ca819 100644
--- a/web-console/package.json
+++ b/web-console/package.json
@@ -76,7 +76,7 @@
"d3-axis": "^2.1.0",
"d3-scale": "^3.3.0",
"d3-selection": "^2.0.0",
- "druid-query-toolkit": "^0.18.3",
+ "druid-query-toolkit": "^0.18.12",
"file-saver": "^2.0.2",
"follow-redirects": "^1.14.7",
"fontsource-open-sans": "^3.0.9",
diff --git a/web-console/src/components/json-collapse/json-collapse.tsx
b/web-console/src/components/json-collapse/json-collapse.tsx
index 3a92db4db6..5a0869a5bd 100644
--- a/web-console/src/components/json-collapse/json-collapse.tsx
+++ b/web-console/src/components/json-collapse/json-collapse.tsx
@@ -31,7 +31,7 @@ export const JsonCollapse = React.memo(function
JsonCollapse(props: JsonCollapse
const { stringValue, buttonText } = props;
const [isOpen, setIsOpen] = useState(false);
- const prettyValue = JSONBig.stringify(JSON.parse(stringValue), undefined, 2);
+ const prettyValue = JSONBig.stringify(JSONBig.parse(stringValue), undefined,
2);
return (
<div className="json-collapse">
<div className="collapse-buttons">
diff --git a/web-console/src/dialogs/spec-dialog/spec-dialog.tsx
b/web-console/src/dialogs/spec-dialog/spec-dialog.tsx
index c4bc5a3fa9..f38cd0d8df 100644
--- a/web-console/src/dialogs/spec-dialog/spec-dialog.tsx
+++ b/web-console/src/dialogs/spec-dialog/spec-dialog.tsx
@@ -21,13 +21,14 @@ import * as JSONBig from 'json-bigint-native';
import React, { useState } from 'react';
import AceEditor from 'react-ace';
-import { validJson } from '../../utils';
+import { AppToaster } from '../../singletons';
+import { offsetToRowColumn } from '../../utils';
import './spec-dialog.scss';
export interface SpecDialogProps {
- onSubmit: (spec: JSON) => void | Promise<void>;
- onClose: () => void;
+ onSubmit(spec: JSON): void | Promise<void>;
+ onClose(): void;
title: string;
initSpec?: any;
}
@@ -38,9 +39,23 @@ export const SpecDialog = React.memo(function
SpecDialog(props: SpecDialogProps)
initSpec ? JSONBig.stringify(initSpec, undefined, 2) : '',
);
- function postSpec(): void {
- if (!validJson(spec)) return;
- void onSubmit(JSON.parse(spec));
+ function handleSubmit(): void {
+ let parsed: any;
+ try {
+ parsed = JSONBig.parse(spec);
+ } catch (e) {
+ const rowColumn = typeof e.at === 'number' ? offsetToRowColumn(spec,
e.at) : undefined;
+ AppToaster.show({
+ intent: Intent.DANGER,
+ message: `Could not parse JSON: ${e.message}${
+ rowColumn ? ` (at line ${rowColumn.row + 1}, column
${rowColumn.column + 1})` : ''
+ }`,
+ timeout: 5000,
+ });
+ return;
+ }
+
+ void onSubmit(parsed);
onClose();
}
@@ -78,12 +93,7 @@ export const SpecDialog = React.memo(function
SpecDialog(props: SpecDialogProps)
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button text="Close" onClick={onClose} />
- <Button
- text="Submit"
- intent={Intent.PRIMARY}
- onClick={postSpec}
- disabled={!validJson(spec)}
- />
+ <Button text="Submit" intent={Intent.PRIMARY} onClick={handleSubmit}
disabled={!spec} />
</div>
</div>
</Dialog>
diff --git a/web-console/src/utils/general.spec.ts
b/web-console/src/utils/general.spec.ts
index fa11e1c871..7b380ae7cf 100644
--- a/web-console/src/utils/general.spec.ts
+++ b/web-console/src/utils/general.spec.ts
@@ -28,6 +28,7 @@ import {
moveElement,
moveToIndex,
objectHash,
+ offsetToRowColumn,
parseCsvLine,
swapElements,
} from './general';
@@ -171,4 +172,19 @@ describe('general', () => {
expect(objectHash({ hello: 'world1' })).toEqual('cc14ad13');
});
});
+
+ describe('offsetToRowColumn', () => {
+ it('works', () => {
+ expect(offsetToRowColumn('Hello\nThis is a test\nstring.',
-6)).toBeUndefined();
+ expect(offsetToRowColumn('Hello\nThis is a test\nstring.',
666)).toBeUndefined();
+ expect(offsetToRowColumn('Hello\nThis is a test\nstring.', 3)).toEqual({
+ column: 3,
+ row: 0,
+ });
+ expect(offsetToRowColumn('Hello\nThis is a test\nstring.', 24)).toEqual({
+ column: 3,
+ row: 2,
+ });
+ });
+ });
});
diff --git a/web-console/src/utils/general.tsx
b/web-console/src/utils/general.tsx
index 3b29dceafd..f7c6823203 100644
--- a/web-console/src/utils/general.tsx
+++ b/web-console/src/utils/general.tsx
@@ -465,3 +465,26 @@ export function tickIcon(checked: boolean): IconName {
export function generate8HexId(): string {
return (Math.random() * 1e10).toString(16).replace('.', '').slice(0, 8);
}
+
+export function offsetToRowColumn(
+ str: string,
+ offset: number,
+): { row: number; column: number } | undefined {
+ // Ensure offset is within the string length
+ if (offset < 0 || offset > str.length) return;
+
+ const lines = str.split('\n');
+ for (let row = 0; row < lines.length; row++) {
+ const line = lines[row];
+ if (offset < line.length) {
+ return {
+ row,
+ column: offset,
+ };
+ }
+
+ offset -= line.length + 1;
+ }
+
+ return;
+}
diff --git a/web-console/src/utils/object-change.spec.ts
b/web-console/src/utils/object-change.spec.ts
index e7c28b2eff..bdfecca7ec 100644
--- a/web-console/src/utils/object-change.spec.ts
+++ b/web-console/src/utils/object-change.spec.ts
@@ -138,7 +138,7 @@ describe('object-change', () => {
});
it('works with arrays', () => {
- expect(JSON.parse(JSONBig.stringify(deepDelete(thing,
'hello.wow.0')))).toEqual({
+ expect(JSONBig.parse(JSONBig.stringify(deepDelete(thing,
'hello.wow.0')))).toEqual({
hello: {
moon: 1,
wow: [
diff --git a/web-console/src/views/load-data-view/filter-table/filter-table.tsx
b/web-console/src/views/load-data-view/filter-table/filter-table.tsx
index 368072762f..3f0323e189 100644
--- a/web-console/src/views/load-data-view/filter-table/filter-table.tsx
+++ b/web-console/src/views/load-data-view/filter-table/filter-table.tsx
@@ -23,7 +23,7 @@ import ReactTable from 'react-table';
import { TableCell } from '../../../components';
import type { DruidFilter } from '../../../druid-models';
-import { getFilterDimension } from '../../../druid-models';
+import { getFilterDimension, TIME_COLUMN } from '../../../druid-models';
import {
DEFAULT_TABLE_CLASS_NAME,
STANDARD_TABLE_PAGE_SIZE,
@@ -67,7 +67,7 @@ export const FilterTable = React.memo(function
FilterTable(props: FilterTablePro
showPagination={sampleResponse.data.length > STANDARD_TABLE_PAGE_SIZE}
columns={filterMap(getHeaderNamesFromSampleResponse(sampleResponse),
(columnName, i) => {
if (!caseInsensitiveContains(columnName, columnFilter)) return;
- const timestamp = columnName === '__time';
+ const isTimestamp = columnName === TIME_COLUMN;
const filterIndex = dimensionFilters.findIndex(f =>
getFilterDimension(f) === columnName);
const filter = dimensionFilters[filterIndex];
@@ -84,7 +84,7 @@ export const FilterTable = React.memo(function
FilterTable(props: FilterTablePro
onFilterSelect(filter, filterIndex);
} else {
onFilterSelect(
- timestamp
+ isTimestamp
? { type: 'interval', dimension: columnName, intervals:
[] }
: { type: 'selector', dimension: columnName, value: '' },
-1,
@@ -102,7 +102,7 @@ export const FilterTable = React.memo(function
FilterTable(props: FilterTablePro
accessor: (row: SampleEntry) => (row.parsed ? row.parsed[columnName]
: null),
width: 140,
Cell: function FilterTableCell(row: RowRenderProps) {
- return <TableCell value={timestamp ? new Date(row.value) :
row.value} />;
+ return <TableCell value={isTimestamp ? new Date(Number(row.value))
: row.value} />;
},
};
})}
diff --git
a/web-console/src/views/load-data-view/parse-time-table/parse-time-table.tsx
b/web-console/src/views/load-data-view/parse-time-table/parse-time-table.tsx
index 61cc4aeecc..2d7a32bad1 100644
--- a/web-console/src/views/load-data-view/parse-time-table/parse-time-table.tsx
+++ b/web-console/src/views/load-data-view/parse-time-table/parse-time-table.tsx
@@ -27,6 +27,7 @@ import {
getTimestampDetailFromSpec,
getTimestampSpecColumnFromSpec,
possibleDruidFormatForValues,
+ TIME_COLUMN,
} from '../../../druid-models';
import {
DEFAULT_TABLE_CLASS_NAME,
@@ -86,7 +87,7 @@ export const ParseTimeTable = React.memo(function
ParseTimeTable(props: ParseTim
pageSizeOptions={STANDARD_TABLE_PAGE_SIZE_OPTIONS}
showPagination={sampleResponse.data.length > STANDARD_TABLE_PAGE_SIZE}
columns={filterMap(getHeaderNamesFromSampleResponse(sampleResponse),
(columnName, i) => {
- const isTimestamp = columnName === '__time';
+ const isTimestamp = columnName === TIME_COLUMN;
if (!isTimestamp && !caseInsensitiveContains(columnName,
columnFilter)) return;
const used = timestampSpecColumn === columnName;
const possibleFormat = isTimestamp
@@ -134,7 +135,7 @@ export const ParseTimeTable = React.memo(function
ParseTimeTable(props: ParseTim
if (row.original.unparseable) {
return <TableCellUnparseable timestamp={isTimestamp} />;
}
- return <TableCell value={isTimestamp ? new Date(row.value) :
row.value} />;
+ return <TableCell value={isTimestamp ? new Date(Number(row.value))
: row.value} />;
},
width: isTimestamp ? 200 : 140,
resizable: !isTimestamp,
diff --git a/web-console/src/views/load-data-view/schema-table/schema-table.tsx
b/web-console/src/views/load-data-view/schema-table/schema-table.tsx
index b6ba0e58d3..b1245d6b39 100644
--- a/web-console/src/views/load-data-view/schema-table/schema-table.tsx
+++ b/web-console/src/views/load-data-view/schema-table/schema-table.tsx
@@ -28,6 +28,7 @@ import {
getDimensionSpecType,
getMetricSpecName,
inflateDimensionSpec,
+ TIME_COLUMN,
} from '../../../druid-models';
import {
DEFAULT_TABLE_CLASS_NAME,
@@ -109,7 +110,7 @@ export const SchemaTable = React.memo(function
SchemaTable(props: SchemaTablePro
},
};
} else {
- const isTimestamp = columnName === '__time';
+ const isTimestamp = columnName === TIME_COLUMN;
const dimensionSpecIndex = dimensions
? dimensions.findIndex(d => getDimensionSpecName(d) === columnName)
: -1;
@@ -151,7 +152,7 @@ export const SchemaTable = React.memo(function
SchemaTable(props: SchemaTablePro
width: isTimestamp ? 200 : 140,
accessor: (row: SampleEntry) => (row.parsed ?
row.parsed[columnName] : null),
Cell: function SchemaTableCell(row: RowRenderProps) {
- return <TableCell value={isTimestamp ? new Date(row.value) :
row.value} />;
+ return <TableCell value={isTimestamp ? new
Date(Number(row.value)) : row.value} />;
},
};
}
diff --git
a/web-console/src/views/load-data-view/transform-table/transform-table.tsx
b/web-console/src/views/load-data-view/transform-table/transform-table.tsx
index 55c4e091fb..02d253d3a5 100644
--- a/web-console/src/views/load-data-view/transform-table/transform-table.tsx
+++ b/web-console/src/views/load-data-view/transform-table/transform-table.tsx
@@ -23,6 +23,7 @@ import ReactTable from 'react-table';
import { TableCell } from '../../../components';
import type { Transform } from '../../../druid-models';
+import { TIME_COLUMN } from '../../../druid-models';
import {
DEFAULT_TABLE_CLASS_NAME,
STANDARD_TABLE_PAGE_SIZE,
@@ -79,7 +80,7 @@ export const TransformTable = React.memo(function
TransformTable(props: Transfor
showPagination={sampleResponse.data.length > STANDARD_TABLE_PAGE_SIZE}
columns={filterMap(getHeaderNamesFromSampleResponse(sampleResponse),
(columnName, i) => {
if (!caseInsensitiveContains(columnName, columnFilter)) return;
- const timestamp = columnName === '__time';
+ const isTimestamp = columnName === TIME_COLUMN;
const transformIndex = transforms.findIndex(f => f.name ===
columnName);
if (transformIndex === -1 && transformedColumnsOnly) return;
const transform = transforms[transformIndex];
@@ -119,7 +120,7 @@ export const TransformTable = React.memo(function
TransformTable(props: Transfor
accessor: (row: SampleEntry) => (row.parsed ? row.parsed[columnName]
: null),
width: 140,
Cell: function TransformTableCell(row: RowRenderProps) {
- return <TableCell value={timestamp ? new Date(row.value) :
row.value} />;
+ return <TableCell value={isTimestamp ? new Date(Number(row.value))
: row.value} />;
},
};
})}
diff --git
a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
index e6cc36ca64..e197e2d87c 100644
---
a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
+++
b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
@@ -17,18 +17,19 @@
*/
import { Button, Classes, Dialog, Intent } from '@blueprintjs/core';
+import * as JSONBig from 'json-bigint-native';
import React, { useState } from 'react';
import AceEditor from 'react-ace';
import { Execution } from '../../../druid-models';
import { AppToaster } from '../../../singletons';
-import { validJson } from '../../../utils';
+import { offsetToRowColumn } from '../../../utils';
import './execution-submit-dialog.scss';
export interface ExecutionSubmitDialogProps {
- onSubmit: (execution: Execution) => void;
- onClose: () => void;
+ onSubmit(execution: Execution): void;
+ onClose(): void;
}
export const ExecutionSubmitDialog = React.memo(function ExecutionSubmitDialog(
@@ -37,15 +38,18 @@ export const ExecutionSubmitDialog = React.memo(function
ExecutionSubmitDialog(
const { onClose, onSubmit } = props;
const [archive, setArchive] = useState('');
- function submitProfile(): void {
- if (!validJson(archive)) return;
+ function handleSubmit(): void {
let parsed: any;
try {
- parsed = JSON.parse(archive);
+ parsed = JSONBig.parse(archive);
} catch (e) {
+ const rowColumn = typeof e.at === 'number' ? offsetToRowColumn(archive,
e.at) : undefined;
AppToaster.show({
intent: Intent.DANGER,
- message: `Could not parse JSON: ${e.message}`,
+ message: `Could not parse JSON: ${e.message}${
+ rowColumn ? ` (at line ${rowColumn.row + 1}, column
${rowColumn.column + 1})` : ''
+ }`,
+ timeout: 5000,
});
return;
}
@@ -129,8 +133,8 @@ export const ExecutionSubmitDialog = React.memo(function
ExecutionSubmitDialog(
<Button
text="Submit"
intent={Intent.PRIMARY}
- onClick={submitProfile}
- disabled={!validJson(archive)}
+ onClick={handleSubmit}
+ disabled={!archive}
/>
</div>
</div>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]