You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by vo...@apache.org on 2023/05/06 00:33:47 UTC

[druid] branch 26.0.0 updated: Web console: misc bug fixes (#14216) (#14217)

This is an automated email from the ASF dual-hosted git repository.

vogievetsky pushed a commit to branch 26.0.0
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/26.0.0 by this push:
     new 399a3c58b6 Web console: misc bug fixes (#14216) (#14217)
399a3c58b6 is described below

commit 399a3c58b60c0be03e2d6f65bc3a3f5466fa47ac
Author: Vadim Ogievetsky <va...@ogievetsky.com>
AuthorDate: Fri May 5 17:33:36 2023 -0700

    Web console: misc bug fixes (#14216) (#14217)
    
    * fixing little things
    
    * clear edit columns when switching to SQL tab
    
    * updated snapshots
---
 .../druid-models/ingestion-spec/ingestion-spec.tsx |  10 +-
 .../react-table-pagination.tsx                     |   6 +-
 web-console/src/utils/sampler.ts                   |   5 +-
 .../__snapshots__/load-data-view.spec.tsx.snap     | 202 ++++++++++++++++++++-
 .../src/views/load-data-view/load-data-view.tsx    |  11 +-
 .../schema-step/schema-step.tsx                    |  26 ++-
 6 files changed, 240 insertions(+), 20 deletions(-)

diff --git a/web-console/src/druid-models/ingestion-spec/ingestion-spec.tsx b/web-console/src/druid-models/ingestion-spec/ingestion-spec.tsx
index 8741668b59..24d52373d0 100644
--- a/web-console/src/druid-models/ingestion-spec/ingestion-spec.tsx
+++ b/web-console/src/druid-models/ingestion-spec/ingestion-spec.tsx
@@ -2154,9 +2154,9 @@ export function issueWithSampleData(
   if (firstData === '{') {
     return (
       <>
-        This data looks like regular JSON object. For Druid to parse a text file it must have one
-        row per event. Maybe look at{' '}
-        <ExternalLink href="http://ndjson.org/">newline delimited JSON</ExternalLink> instead.
+        This data looks like multi-line formatted JSON object. For Druid to parse a text file it
+        must have one row per event. Consider reformatting your data as{' '}
+        <ExternalLink href="http://ndjson.org/">newline delimited JSON</ExternalLink>.
       </>
     );
   }
@@ -2165,8 +2165,8 @@ export function issueWithSampleData(
     return (
       <>
         This data looks like a multi-line JSON array. For Druid to parse a text file it must have
-        one row per event. Maybe look at{' '}
-        <ExternalLink href="http://ndjson.org/">newline delimited JSON</ExternalLink> instead.
+        one row per event. Consider reformatting your data as{' '}
+        <ExternalLink href="http://ndjson.org/">newline delimited JSON</ExternalLink>.
       </>
     );
   }
diff --git a/web-console/src/react-table/react-table-pagination/react-table-pagination.tsx b/web-console/src/react-table/react-table-pagination/react-table-pagination.tsx
index 956cd7f8a2..04b2b0ea02 100644
--- a/web-console/src/react-table/react-table-pagination/react-table-pagination.tsx
+++ b/web-console/src/react-table/react-table-pagination/react-table-pagination.tsx
@@ -100,8 +100,8 @@ export const ReactTablePagination = React.memo(function ReactTablePagination(
 
   const start = page * pageSize + 1;
   let end = page * pageSize + pageSize;
-  if (nonEmptyArray(sortedData)) {
-    end = Math.min(end, page * pageSize + sortedData.length);
+  if (nonEmptyArray(sortedData) && (page === 0 || sortedData.length > pageSize)) {
+    end = Math.min(end, sortedData.length);
   }
 
   let pageInfo = 'Showing';
@@ -110,7 +110,7 @@ export const ReactTablePagination = React.memo(function ReactTablePagination(
   } else {
     pageInfo += '...';
   }
-  if (ofText && Array.isArray(sortedData)) {
+  if (ofText && nonEmptyArray(sortedData)) {
     pageInfo += ` ${ofText} ${formatInteger(sortedData.length)}`;
   }
 
diff --git a/web-console/src/utils/sampler.ts b/web-console/src/utils/sampler.ts
index 67f883a580..622584e667 100644
--- a/web-console/src/utils/sampler.ts
+++ b/web-console/src/utils/sampler.ts
@@ -171,7 +171,10 @@ export async function getProxyOverlordModules(): Promise<string[]> {
     throw new Error(getDruidErrorMessage(e));
   }
 
-  return statusResp.data.modules.map((m: any) => m.artifact);
+  const { modules } = statusResp.data;
+  if (!Array.isArray(modules)) throw new Error('unexpected result from overlord/status');
+
+  return modules.map((m: any) => m.artifact);
 }
 
 export async function postToSampler(
diff --git a/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap b/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
index 9cf8d12a9d..f5956786af 100644
--- a/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
+++ b/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
@@ -159,7 +159,148 @@ exports[`LoadDataView matches snapshot batch 1`] = `
       <div
         className="ingestion-cards"
       >
-        <React.Fragment />
+        <React.Fragment>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for index_parallel:s3"
+              src="/some/base_url/assets/s3.png"
+            />
+            <p>
+              Amazon S3
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for index_parallel:azure"
+              src="/some/base_url/assets/azure.png"
+            />
+            <p>
+              Azure Data Lake
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for index_parallel:google"
+              src="/some/base_url/assets/google.png"
+            />
+            <p>
+              Google Cloud Storage
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for index_parallel:hdfs"
+              src="/some/base_url/assets/hdfs.png"
+            />
+            <p>
+              HDFS
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for index_parallel:druid"
+              src="/some/base_url/assets/druid.png"
+            />
+            <p>
+              Reindex from Druid
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for index_parallel:http"
+              src="/some/base_url/assets/http.png"
+            />
+            <p>
+              HTTP(s)
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for index_parallel:local"
+              src="/some/base_url/assets/local.png"
+            />
+            <p>
+              Local disk
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for index_parallel:inline"
+              src="/some/base_url/assets/inline.png"
+            />
+            <p>
+              Paste data
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for example"
+              src="/some/base_url/assets/example.png"
+            />
+            <p>
+              Example data
+            </p>
+          </Blueprint4.Card>
+        </React.Fragment>
+        <Blueprint4.Card
+          className="ingestion-card"
+          elevation={1}
+          interactive={true}
+          onClick={[Function]}
+        >
+          <img
+            alt="Ingestion tile for other"
+            src="/some/base_url/assets/other.png"
+          />
+          <p>
+            Other
+          </p>
+        </Blueprint4.Card>
       </div>
     </div>
     <div
@@ -336,7 +477,64 @@ exports[`LoadDataView matches snapshot streaming 1`] = `
       <div
         className="ingestion-cards"
       >
-        <React.Fragment />
+        <React.Fragment>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for kafka"
+              src="/some/base_url/assets/kafka.png"
+            />
+            <p>
+              Apache Kafka
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for kinesis"
+              src="/some/base_url/assets/kinesis.png"
+            />
+            <p>
+              Amazon Kinesis
+            </p>
+          </Blueprint4.Card>
+          <Blueprint4.Card
+            className="ingestion-card"
+            elevation={1}
+            interactive={true}
+            onClick={[Function]}
+          >
+            <img
+              alt="Ingestion tile for azure-event-hubs"
+              src="/some/base_url/assets/azure-event-hubs.png"
+            />
+            <p>
+              Azure Event Hub
+            </p>
+          </Blueprint4.Card>
+        </React.Fragment>
+        <Blueprint4.Card
+          className="ingestion-card"
+          elevation={1}
+          interactive={true}
+          onClick={[Function]}
+        >
+          <img
+            alt="Ingestion tile for other"
+            src="/some/base_url/assets/other.png"
+          />
+          <p>
+            Other
+          </p>
+        </Blueprint4.Card>
       </div>
     </div>
     <div
diff --git a/web-console/src/views/load-data-view/load-data-view.tsx b/web-console/src/views/load-data-view/load-data-view.tsx
index b332d277d3..6ea6946759 100644
--- a/web-console/src/views/load-data-view/load-data-view.tsx
+++ b/web-console/src/views/load-data-view/load-data-view.tsx
@@ -498,10 +498,10 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
       overlordModules = await getProxyOverlordModules();
     } catch (e) {
       AppToaster.show({
-        message: `Failed to get overlord modules: ${e.message}`,
+        message: `Failed to get the list of loaded modules from the overlord: ${e.message}`,
         intent: Intent.DANGER,
       });
-      this.setState({ overlordModules: [] });
+      this.setState({ overlordModules: undefined });
       return;
     }
 
@@ -810,9 +810,10 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
     disabled?: boolean,
   ): JSX.Element | undefined {
     const { overlordModules, selectedComboType, spec } = this.state;
-    if (!overlordModules) return;
     const requiredModule = getRequiredModule(comboType);
-    const goodToGo = !disabled && (!requiredModule || overlordModules.includes(requiredModule));
+    const goodToGo =
+      !disabled &&
+      (!requiredModule || !overlordModules || overlordModules.includes(requiredModule));
 
     return (
       <Card
@@ -1124,7 +1125,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
         <p>
           Please make sure that the
           <Code>&quot;{requiredModule}&quot;</Code> extension is included in the{' '}
-          <Code>loadList</Code>.
+          <Code>druid.extensions.loadList</Code>.
         </p>
         <p>
           For more information please refer to the{' '}
diff --git a/web-console/src/views/sql-data-loader-view/schema-step/schema-step.tsx b/web-console/src/views/sql-data-loader-view/schema-step/schema-step.tsx
index 7446b60550..00b1e1ff2f 100644
--- a/web-console/src/views/sql-data-loader-view/schema-step/schema-step.tsx
+++ b/web-console/src/views/sql-data-loader-view/schema-step/schema-step.tsx
@@ -299,12 +299,30 @@ export const SchemaStep = function SchemaStep(props: SchemaStepProps) {
     onQueryStringChange(parsedQuery.apply(queryAction).toString());
   });
 
+  const handleModeSelect = (newMode: Mode) => {
+    if (newMode === 'sql' && editorColumn) {
+      if (editorColumn.dirty) {
+        AppToaster.show({
+          message:
+            'Please save or discard the changes in the column editor before switching to the SQL tab.',
+          intent: Intent.WARNING,
+        });
+        return;
+      }
+
+      setEditorColumn(undefined);
+    }
+
+    setMode(newMode);
+  };
+
   const handleColumnSelect = usePermanentCallback((index: number) => {
     if (!ingestQueryPattern) return;
 
     if (editorColumn?.dirty) {
       AppToaster.show({
-        message: 'Please save or discard the changes in the column editor.',
+        message:
+          'Please save or discard the changes in the column editor before switching columns.',
         intent: Intent.WARNING,
       });
       return;
@@ -697,20 +715,20 @@ export const SchemaStep = function SchemaStep(props: SchemaStepProps) {
                 text="Table"
                 disabled={!ingestQueryPattern}
                 active={effectiveMode === 'table'}
-                onClick={() => setMode('table')}
+                onClick={() => handleModeSelect('table')}
               />
               <Button
                 icon={IconNames.LIST_COLUMNS}
                 text="List"
                 disabled={!ingestQueryPattern}
                 active={effectiveMode === 'list'}
-                onClick={() => setMode('list')}
+                onClick={() => handleModeSelect('list')}
               />
               <Button
                 icon={IconNames.APPLICATION}
                 text="SQL"
                 active={effectiveMode === 'sql'}
-                onClick={() => setMode('sql')}
+                onClick={() => handleModeSelect('sql')}
               />
             </ButtonGroup>
             {enableAnalyze && ingestQueryPattern?.metrics && (


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org