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 ee06137787 better native json error UX (#14155)
ee06137787 is described below

commit ee06137787af0b090fbee57d2b21d612fe8dbadd
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Tue Apr 25 10:30:42 2023 -0700

    better native json error UX (#14155)
---
 .../workbench-query/workbench-query-part.ts        |  8 +++---
 .../workbench-query/workbench-query.spec.ts        | 33 ++++++++++++++++++++++
 .../workbench-query/workbench-query.ts             | 17 +++++++----
 .../workbench-view/helper-query/helper-query.tsx   | 23 +++++++++++++--
 .../views/workbench-view/query-tab/query-tab.tsx   | 19 ++++++++++++-
 5 files changed, 86 insertions(+), 14 deletions(-)

diff --git 
a/web-console/src/druid-models/workbench-query/workbench-query-part.ts 
b/web-console/src/druid-models/workbench-query/workbench-query-part.ts
index 680a0780bd..71fe3fa1f3 100644
--- a/web-console/src/druid-models/workbench-query/workbench-query-part.ts
+++ b/web-console/src/druid-models/workbench-query/workbench-query-part.ts
@@ -144,13 +144,13 @@ export class WorkbenchQueryPart {
     return this.queryString.trim().startsWith('{');
   }
 
-  public validJson(): boolean {
+  public issueWithJson(): string | undefined {
     try {
       Hjson.parse(this.queryString);
-      return true;
-    } catch {
-      return false;
+    } catch (e) {
+      return e.message;
     }
+    return;
   }
 
   public isSqlInJson(): boolean {
diff --git 
a/web-console/src/druid-models/workbench-query/workbench-query.spec.ts 
b/web-console/src/druid-models/workbench-query/workbench-query.spec.ts
index cc57096c43..cef773abb0 100644
--- a/web-console/src/druid-models/workbench-query/workbench-query.spec.ts
+++ b/web-console/src/druid-models/workbench-query/workbench-query.spec.ts
@@ -127,6 +127,24 @@ describe('WorkbenchQuery', () => {
     expect(String(WorkbenchQuery.fromString(tabString))).toEqual(tabString);
   });
 
+  describe('.getRowColumnFromIssue', () => {
+    it('works when it can not find at line', () => {
+      expect(WorkbenchQuery.getRowColumnFromIssue(`lol`)).toBeUndefined();
+    });
+
+    it('works when it can find at line', () => {
+      expect(
+        WorkbenchQuery.getRowColumnFromIssue(
+          `End of input while parsing an object (missing '}') at line 40,2 
>>>} ...`,
+        ),
+      ).toEqual({
+        match: '',
+        row: 39,
+        column: 1,
+      });
+    });
+  });
+
   describe('#makePreview', () => {
     it('works', () => {
       const workbenchQuery = WorkbenchQuery.blank().changeQueryString(sane`
@@ -640,4 +658,19 @@ describe('WorkbenchQuery', () => {
       `);
     });
   });
+
+  describe('#getIssue', () => {
+    it('works', () => {
+      expect(
+        WorkbenchQuery.blank()
+          .changeQueryString(
+            sane`
+              {
+                lol: 1
+            `,
+          )
+          .getIssue(),
+      ).toEqual("End of input while parsing an object (missing '}') at line 
2,9 >>>  lol: 1 ...");
+    });
+  });
 });
diff --git a/web-console/src/druid-models/workbench-query/workbench-query.ts 
b/web-console/src/druid-models/workbench-query/workbench-query.ts
index 1a3d1e98ec..68d2dcd71f 100644
--- a/web-console/src/druid-models/workbench-query/workbench-query.ts
+++ b/web-console/src/druid-models/workbench-query/workbench-query.ts
@@ -34,7 +34,7 @@ import Hjson from 'hjson';
 import * as JSONBig from 'json-bigint-native';
 import { v4 as uuidv4 } from 'uuid';
 
-import type { ColumnMetadata } from '../../utils';
+import type { ColumnMetadata, RowColumn } from '../../utils';
 import { deleteKeys, generate8HexId } from '../../utils';
 import type { DruidEngine } from '../druid-engine/druid-engine';
 import { validDruidEngine } from '../druid-engine/druid-engine';
@@ -225,6 +225,12 @@ export class WorkbenchQuery {
     return orderByExpressions.length ? 
SqlOrderByClause.create(orderByExpressions) : undefined;
   }
 
+  static getRowColumnFromIssue(issue: string): RowColumn | undefined {
+    const m = issue.match(/at line (\d+),(\d+)/);
+    if (!m) return;
+    return { match: '', row: Number(m[1]) - 1, column: Number(m[2]) - 1 };
+  }
+
   public readonly queryParts: WorkbenchQueryPart[];
   public readonly queryContext: QueryContext;
   public readonly engine?: DruidEngine;
@@ -351,13 +357,12 @@ export class WorkbenchQuery {
     return this.getLastPart().isEmptyQuery();
   }
 
-  public isValid(): boolean {
+  public getIssue(): string | undefined {
     const lastPart = this.getLastPart();
-    if (lastPart.isJsonLike() && !lastPart.validJson()) {
-      return false;
+    if (lastPart.isJsonLike()) {
+      return lastPart.issueWithJson();
     }
-
-    return true;
+    return;
   }
 
   public canPrettify(): boolean {
diff --git a/web-console/src/views/workbench-view/helper-query/helper-query.tsx 
b/web-console/src/views/workbench-view/helper-query/helper-query.tsx
index cc7f638c25..f9711ab5cd 100644
--- a/web-console/src/views/workbench-view/helper-query/helper-query.tsx
+++ b/web-console/src/views/workbench-view/helper-query/helper-query.tsx
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-import { Button, ButtonGroup, InputGroup, Menu, MenuItem } from 
'@blueprintjs/core';
+import { Button, ButtonGroup, InputGroup, Intent, Menu, MenuItem } from 
'@blueprintjs/core';
 import { IconNames } from '@blueprintjs/icons';
 import { Popover2 } from '@blueprintjs/popover2';
 import axios from 'axios';
@@ -40,7 +40,7 @@ import {
   submitTaskQuery,
 } from '../../../helpers';
 import { usePermanentCallback, useQueryManager } from '../../../hooks';
-import { Api } from '../../../singletons';
+import { Api, AppToaster } from '../../../singletons';
 import { ExecutionStateCache } from 
'../../../singletons/execution-state-cache';
 import { WorkbenchHistory } from '../../../singletons/workbench-history';
 import type { WorkbenchRunningPromise } from 
'../../../singletons/workbench-running-promises';
@@ -251,7 +251,24 @@ export const HelperQuery = React.memo(function 
HelperQuery(props: HelperQueryPro
   }
 
   const handleRun = usePermanentCallback(async (preview: boolean) => {
-    if (!query.isValid()) return;
+    const queryIssue = query.getIssue();
+    if (queryIssue) {
+      const position = WorkbenchQuery.getRowColumnFromIssue(queryIssue);
+
+      AppToaster.show({
+        icon: IconNames.ERROR,
+        intent: Intent.DANGER,
+        timeout: 90000,
+        message: queryIssue,
+        action: position
+          ? {
+              text: 'Go to issue',
+              onClick: () => moveToPosition(position),
+            }
+          : undefined,
+      });
+      return;
+    }
 
     if (query.getEffectiveEngine() !== 'sql-msq-task') {
       WorkbenchHistory.addQueryToHistory(query);
diff --git a/web-console/src/views/workbench-view/query-tab/query-tab.tsx 
b/web-console/src/views/workbench-view/query-tab/query-tab.tsx
index 3fe362ab48..cc74617910 100644
--- a/web-console/src/views/workbench-view/query-tab/query-tab.tsx
+++ b/web-console/src/views/workbench-view/query-tab/query-tab.tsx
@@ -274,7 +274,24 @@ export const QueryTab = React.memo(function 
QueryTab(props: QueryTabProps) {
   }
 
   const handleRun = usePermanentCallback(async (preview: boolean) => {
-    if (!query.isValid()) return;
+    const queryIssue = query.getIssue();
+    if (queryIssue) {
+      const position = WorkbenchQuery.getRowColumnFromIssue(queryIssue);
+
+      AppToaster.show({
+        icon: IconNames.ERROR,
+        intent: Intent.DANGER,
+        timeout: 90000,
+        message: queryIssue,
+        action: position
+          ? {
+              text: 'Go to issue',
+              onClick: () => moveToPosition(position),
+            }
+          : undefined,
+      });
+      return;
+    }
 
     if (query.getEffectiveEngine() !== 'sql-msq-task') {
       WorkbenchHistory.addQueryToHistory(query);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to