Browse Source

[ui-newImporter]Skeleton and Importer type chooser (#4077)

Nidhi Bhat G 7 tháng trước cách đây
mục cha
commit
52f174f24e

+ 7 - 0
desktop/core/src/desktop/conf.py

@@ -1892,6 +1892,13 @@ ENABLE_NEW_STORAGE_BROWSER = Config(
   default=False
 )
 
+ENABLE_NEW_IMPORTER = Config(
+  key="enable_new_importer",
+  help=_("Feature flag to enable new Hue Importer."),
+  type=coerce_bool,
+  default=False
+)
+
 
 def is_chunked_fileuploader_enabled():
   return ENABLE_CHUNKED_FILE_UPLOADER.get()

+ 30 - 0
desktop/core/src/desktop/js/apps/newimporter/ImporterPage.scss

@@ -0,0 +1,30 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@use 'variables' as vars;
+@use 'mixins';
+
+.hue-importer.antd.cuix {
+  @include mixins.fillAbsolute;
+  @include mixins.flexRowLayout;
+
+  .hue-importer__container {
+    background-color: vars.$fluidx-gray-100;
+    height: 100%;
+    overflow: auto;
+  }
+}
+

+ 39 - 0
desktop/core/src/desktop/js/apps/newimporter/ImporterPage.tsx

@@ -0,0 +1,39 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React from 'react';
+import DataInIcon from '@cloudera/cuix-core/icons/react/DataInIcon';
+
+import { i18nReact } from '../../utils/i18nReact';
+import CommonHeader from '../../reactComponents/CommonHeader/CommonHeader';
+import ImporterSourceSelector from './ImporterSourceSelector/ImporterSourceSelector';
+
+import './ImporterPage.scss';
+
+const ImporterPage = (): JSX.Element => {
+  const { t } = i18nReact.useTranslation();
+
+  return (
+    <div className="hue-importer cuix antd">
+      <CommonHeader title={t('Importer')} icon={<DataInIcon />} />
+      <div className="hue-importer__container">
+        <ImporterSourceSelector />
+      </div>
+    </div>
+  );
+};
+
+export default ImporterPage;

+ 73 - 0
desktop/core/src/desktop/js/apps/newimporter/ImporterSourceSelector/ImporterSourceSelector.scss

@@ -0,0 +1,73 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@use 'variables' as vars;
+@use 'mixins';
+
+$option-size: 80px;
+
+.antd.cuix {
+    .hue-importer__source-selector{
+        margin: vars.$fluidx-spacing-m;
+        background-color: white;
+        padding: vars.$fluidx-spacing-m 0;
+
+        &-title {
+            display: flex;
+            justify-content: center;  
+            align-items: center;
+            font-size: vars.$fluidx-heading-h3-size;
+            line-height: vars.$fluidx-heading-h3-line-height;
+            font-weight: vars.$fluidx-heading-h3-weight;
+        }
+
+        &-options {
+            display: flex;
+            flex-flow: row wrap;
+            justify-content: center; 
+            margin: 0 auto;
+            margin-top: 70px;
+            max-width: 80%;
+            gap: $option-size;     
+        }
+
+        &-option {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+            width: $option-size;
+
+            &-button {
+                height: $option-size;
+                width: 100%;
+
+                >svg {
+                    height: 40px;
+                }
+            }
+
+            &-btn-title {
+                margin-top: 14px;
+                text-align: center;
+            }
+
+            &-upload {
+                display: none;
+            }
+        }
+    }
+}

+ 116 - 0
desktop/core/src/desktop/js/apps/newimporter/ImporterSourceSelector/ImporterSourceSelector.tsx

@@ -0,0 +1,116 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React, { useRef } from 'react';
+import Button from 'cuix/dist/components/Button';
+import DocumentationIcon from '@cloudera/cuix-core/icons/react/DocumentationIcon';
+
+import { hueWindow } from 'types/types';
+import { i18nReact } from '../../../utils/i18nReact';
+import { UPLOAD_LOCAL_FILE_API_URL } from '../api';
+import useSaveData from '../../../utils/hooks/useSaveData/useSaveData';
+import huePubSub from '../../../utils/huePubSub';
+
+import './ImporterSourceSelector.scss';
+
+const ImporterSourceSelector = (): JSX.Element => {
+  const { t } = i18nReact.useTranslation();
+  const uploadRef = useRef<HTMLInputElement>(null);
+
+  const { save: upload } = useSaveData(UPLOAD_LOCAL_FILE_API_URL, {
+    postOptions: {
+      qsEncodeData: false,
+      headers: {
+        'Content-Type': 'multipart/form-data'
+      }
+    }
+  });
+
+  const handleUploadClick = () => {
+    if (!uploadRef || !uploadRef.current) {
+      return;
+    }
+    uploadRef.current.click();
+  };
+
+  const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const files = e.target.files;
+    if (!files) {
+      return;
+    }
+
+    const file = files[0];
+
+    const payload = new FormData();
+    payload.append('file', file);
+
+    const file_size = file.size;
+    if (file_size === 0) {
+      huePubSub.publish('hue.global.warning', {
+        message: t('This file is empty, please select another file.')
+      });
+    } else if (file_size > 200 * 1000) {
+      huePubSub.publish('hue.global.warning', {
+        message: t(
+          'File size exceeds the supported size (200 KB). Please use the S3, ABFS or HDFS browser to upload files.'
+        )
+      });
+    } else {
+      upload(payload, {
+        onSuccess: () => {
+          //TODO: Send response to preview page
+          // console.log(response);
+        },
+        onError: error => {
+          huePubSub.publish('hue.error', error);
+        }
+      });
+    }
+  };
+
+  return (
+    <div className="hue-importer__source-selector cuix antd">
+      <div className="hue-importer__source-selector-title">
+        {t('Select a source to import from')}
+      </div>
+      <div className="hue-importer__source-selector-options">
+        {(window as hueWindow).ENABLE_DIRECT_UPLOAD && (
+          <div className="hue-importer__source-selector-option">
+            <Button
+              className="hue-importer__source-selector-option-button"
+              size="large"
+              icon={<DocumentationIcon />}
+              data-event={''}
+              onClick={handleUploadClick}
+            ></Button>
+            <span className="hue-importer__source-selector-option-btn-title">
+              {t('Upload from File')}
+            </span>
+            <input
+              ref={uploadRef}
+              type="file"
+              className="hue-importer__source-selector-option-upload"
+              onChange={handleFileUpload}
+              accept=".csv, .xlsx, .xls"
+            />
+          </div>
+        )}
+      </div>
+    </div>
+  );
+};
+
+export default ImporterSourceSelector;

+ 17 - 0
desktop/core/src/desktop/js/apps/newimporter/api.ts

@@ -0,0 +1,17 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export const UPLOAD_LOCAL_FILE_API_URL = '/indexer/api/indexer/upload_local_file';

+ 2 - 0
desktop/core/src/desktop/js/components/sidebar/HueSidebar.vue

@@ -97,6 +97,7 @@
     markdown: `<svg class="hi hi-fw"><use href="#hi-markdown"></use></svg>`,
     notebook: `<svg class="hi hi-fw"><use href="#hi-file-notebook"></use></svg>`,
     newfilebrowser: `<svg class="hi hi-fw"><use href="#hi-data-browser"></use></svg>`,
+    newimporter: `<svg class="hi hi-fw"><use href="#hi-data-in"></use></svg>`,
     oozie: `<svg class="hi hi-fw"><use href="#hi-oozie"></use></svg>`,
     'oozie-bundle': `<svg class="hi hi-fw"><use href="#hi-oozie-bundle"></use></svg>`,
     'oozie-coordinator': `<svg class="hi hi-fw"><use href="#hi-oozie-coordinator"></use></svg>`,
@@ -362,6 +363,7 @@
             break;
           case 'hbase':
           case 'importer':
+          case 'newimporter':
           case 'indexes':
           case 'kafka':
             break;

+ 11 - 0
desktop/core/src/desktop/js/onePageViewModel.js

@@ -30,6 +30,7 @@ import getParameter from 'utils/url/getParameter';
 import getSearchParameter from 'utils/url/getSearchParameter';
 import { ASSIST_GET_DATABASE_EVENT, ASSIST_GET_SOURCE_EVENT } from 'ko/components/assist/events';
 import { GLOBAL_ERROR_TOPIC } from 'reactComponents/GlobalAlert/events';
+import ImporterPage from '../js/apps/newimporter/ImporterPage';
 
 class OnePageViewModel {
   constructor() {
@@ -786,6 +787,16 @@ class OnePageViewModel {
       { url: '/indexer/indexes/*', app: 'indexes' },
       { url: '/indexer/', app: 'indexes' },
       { url: '/indexer/importer/', app: 'importer' },
+      {
+        url: '/newimporter/',
+        app: function () {
+          showReactAppPage({
+            appName: 'newimporter',
+            component: ImporterPage,
+            title: 'New Importer'
+          });
+        }
+      },
       {
         url: '/indexer/importer/prefill/*',
         app: function (ctx) {

+ 12 - 4
desktop/core/src/desktop/js/reactComponents/CommonHeader/CommonHeader.scss

@@ -15,24 +15,32 @@
 // limitations under the License.
 
 @use 'variables' as vars;
+$icon-height: 30px;
+$icon-width: 30px;
 
 .antd.cuix {
   .hue-common-header {
     background-color: vars.$fluidx-gray-100;
-    padding: vars.$fluidx-spacing-s vars.$fluidx-spacing-s vars.$fluidx-spacing-xs;
+    padding: vars.$fluidx-spacing-s 0 vars.$fluidx-spacing-xs vars.$fluidx-spacing-m;
     display: flex;
   }
 
-  .hue-header-icon > svg {
+  .hue-header-icon {
+    line-height: vars.$fluidx-heading-h2-line-height;
     margin-right: 10px;
     flex: 0 0 auto;
-    height: vars.$fluidx-heading-h2-line-height;
-    color: vars.$fluidx-gray-700;
+
+    > svg {
+      vertical-align: middle;
+      height: $icon-height;
+      width: $icon-width;
+    }
   }
 
   .hue-header-title {
     flex: 0 0 auto;
     font-size: vars.$fluidx-heading-h2-size;
+    margin: 0;
     line-height: vars.$fluidx-heading-h2-line-height;
     font-weight: vars.$fluidx-heading-h2-weight;
     color: vars.$text-color;

+ 1 - 0
desktop/core/src/desktop/js/types/types.ts

@@ -52,6 +52,7 @@ export interface hueWindow {
   ENABLE_HELP_MENU?: boolean;
   ENABLE_PREDICT?: boolean;
   ENABLE_SQL_SYNTAX_CHECK?: boolean;
+  ENABLE_DIRECT_UPLOAD?: boolean;
   HAS_CATALOG?: boolean;
   HAS_CONNECTORS?: boolean;
   HAS_SQL_ANALYZER?: boolean;

+ 17 - 7
desktop/core/src/desktop/models.py

@@ -43,6 +43,7 @@ from desktop.conf import (
   COLLECT_USAGE,
   DISABLE_SOURCE_AUTOCOMPLETE,
   ENABLE_CONNECTORS,
+  ENABLE_NEW_IMPORTER,
   ENABLE_NEW_STORAGE_BROWSER,
   ENABLE_ORGANIZATIONS,
   ENABLE_PROMETHEUS,
@@ -2154,13 +2155,22 @@ class ClusterConfig(object):
         ENABLE_DIRECT_UPLOAD.get()
         ) \
         and 'importer' not in APP_BLACKLIST.get():
-      interpreters.append({
-        'type': 'importer',
-        'displayName': _('Importer'),
-        'buttonName': _('Import'),
-        'tooltip': _('Importer'),
-        'page': '/indexer/importer'
-      })
+        if ENABLE_NEW_IMPORTER.get():
+          interpreters.append({
+            'type': 'newimporter',
+            'displayName': _('New Importer'),
+            'buttonName': _('Import'),
+            'tooltip': _('New Importer'),
+            'page': '/newimporter'
+          })
+
+        interpreters.append({
+          'type': 'importer',
+          'displayName': _('Importer'),
+          'buttonName': _('Import'),
+          'tooltip': _('Importer'),
+          'page': '/indexer/importer'
+        })
 
     if 'sqoop' in self.apps:
       from sqoop.conf import IS_ENABLED

+ 1 - 0
desktop/core/src/desktop/templates/hue.mako

@@ -226,6 +226,7 @@ ${ hueIcons.symbols() }
         <div id="embeddable_indexer" class="embeddable"></div>
         <div id="embeddable_kafka" class="embeddable"></div>
         <div id="embeddable_importer" class="embeddable"></div>
+        <div id="embeddable_newimporter" class="embeddable"></div>
         <div id="embeddable_collections" class="embeddable"></div>
         <div id="embeddable_indexes" class="embeddable"></div>
         <div id="embeddable_useradmin_users" class="embeddable"></div>

+ 4 - 0
desktop/core/src/desktop/templates/hue_icons.mako

@@ -48,6 +48,10 @@
       <path d="M13.65 11.112a3.031 3.031 0 010 6.062 3.03 3.03 0 01-3.021-3.036 3.028 3.028 0 013.021-3.026zm4.135 5.79a4.973 4.973 0 00-4.135-7.729c-2.73 0-4.957 2.23-4.957 4.965 0 2.745 2.227 4.976 4.957 4.976a4.961 4.961 0 002.76-.834l3.215 3.22L21 20.123l-3.215-3.221zM5.198 4.799c.735-.282 2.236-.592 4.464-.592 2.226 0 3.727.31 4.463.592-2.372.765-6.555.765-8.927 0zM9.662 2.5C7.657 2.5 3 2.752 3 5.002v10.281c0 .95.833 1.648 2.459 2.056.959.243 2.188.398 3.515.436a5.88 5.88 0 01-.939-1.755c-1.976-.155-3.099-.562-3.331-.815v-1.881c.871.281 1.917.475 3.03.572.02-.505.098-.979.242-1.445-1.268-.098-2.439-.33-3.272-.679V9.648c1.219.398 2.807.62 4.425.659a6.107 6.107 0 011.926-1.503c-2.295.164-4.86-.078-6.351-.699V6.166c1.355.446 3.156.669 4.958.669 1.8 0 3.601-.223 4.957-.669V8.29c.29.039.581.106.852.204.3.097.58.213.852.348v-3.84c0-2.25-4.657-2.502-6.661-2.502z"></path>
     </symbol>
 
+    <symbol id="hi-data-in" viewBox="0 0 24 24">
+      <path d="M19.346 6.448c-1.462 2.832-1.462 8.273 0 11.105.438-.963.904-2.841.904-5.553 0-2.712-.466-4.59-.904-5.552zM9.446 4h9.52c4.046 0 4.046 16 0 16h-9.52c-1.911 0-3.024-2.767-3.345-6H7.87c.122 1.092.328 2.096.626 2.874.398 1.04.812 1.376.95 1.376h8.29c-1.671-3.373-1.671-9.127 0-12.5h-8.29c-.404 0-1.273 1.49-1.58 4.25H6.102c.32-3.232 1.434-6 3.345-6zm.693 12.235l-1.357-1.47L10.693 13H2v-2h8.693l-1.91-1.765 1.356-1.47L14.723 12l-4.584 4.235z"></path>
+    </symbol>
+
     <symbol id="hi-documents" viewBox="0 0 640 640">
       <path d="M106.54,602.44A36.4,36.4,0,0,1,71,574.3L1,278.19a36.54,36.54,0,0,1,35.56-44.94h47.5V189a33.29,33.29,0,0,1,33.25-33.25H217.62l22.15-91.63a34.55,34.55,0,0,1,41.57-25.59L492.62,88.43a34.76,34.76,0,0,1,21.77,16l30.48,51.29h58.6A36.58,36.58,0,0,1,640,192.24V564.59a36.3,36.3,0,0,1-10.7,25.84l-1.9,1.9A34.31,34.31,0,0,1,603,602.44Zm476.33-41L530.36,339.24H259a36.41,36.41,0,0,1-35.56-28.13l-8.71-36.84H42.21l67.87,287.16ZM561.14,310.37a36.53,36.53,0,0,1,8.32,16L599,451.31V196.72H569.24l6.22,10.46a34.4,34.4,0,0,1,3.92,25.61Zm-39.27-12.15,9-38.1L449,240.79a34.61,34.61,0,0,1-25.69-41.59l19-80.51L278.15,79.88,239.44,240a36.67,36.67,0,0,1,14.42,21.43l8.71,36.84Zm-323-65,8.83-36.53H125.06v36.53ZM534.71,218.9l-52.86-89-17.12,72.42Z"></path>
     </symbol>

+ 1 - 0
desktop/core/src/desktop/urls.py

@@ -34,6 +34,7 @@ from desktop.conf import ANALYTICS, ENABLE_PROMETHEUS, METRICS, SLACK, USE_NEW_E
 from desktop.configuration import api as desktop_configuration_api
 from desktop.lib.vcs import api as desktop_lib_vcs_api
 from desktop.settings import is_oidc_configured
+from indexer import views as indexer_views
 from notebook import views as notebook_views
 from useradmin import views as useradmin_views