Skip to content

[ui-importer] Add configured filesystems in importer #4131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

nidhibhatg
Copy link
Collaborator

@nidhibhatg nidhibhatg commented Apr 30, 2025

What changes were proposed in this pull request?

  • Adds configured filesystems in importer and displays all the filesystems
  • Modifies file chooser to support upload and create folder
  • Adds functionality to click files for importer use case
  • Style improvements
Screenshot 2025-04-30 at 3 08 47 PM Screenshot 2025-04-30 at 3 09 15 PM Screenshot 2025-04-30 at 3 09 31 PM Screenshot 2025-04-30 at 3 10 18 PM Screenshot 2025-05-27 at 12 43 41 PM Screenshot 2025-05-27 at 12 44 16 PM

How was this patch tested?

  • unit tests and manually

Please review Hue Contributing Guide before opening a pull request.

Copy link

github-actions bot commented Apr 30, 2025

✅ Test files were modified. Ensure that the tests cover all relevant changes. ✅

@nidhibhatg nidhibhatg force-pushed the nidhi_importer_filesystems branch from 465c3d9 to 42dccf4 Compare April 30, 2025 09:55
Copy link

github-actions bot commented Apr 30, 2025

Python Code Coverage

Python Coverage Report •
FileStmtsMissCoverMissing
TOTAL537342701349% 
report-only-changed-files is enabled. No files were changed during this commit :)

Pytest Report

Tests Skipped Failures Errors Time
1090 106 💤 0 ❌ 0 🔥 6m 2s ⏱️

Copy link
Collaborator

@ramprasadagarwal ramprasadagarwal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work.
Please check the comments below.

}}
/>
)}
<InputModal
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we refactor the **/StorageDirectoryActions/CreateAndUpload/CreateAndUploadAction.tsx component to take the create file and folder action to separate component and reuse it here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be added in a separate PR

@bjornalm
Copy link
Collaborator

bjornalm commented May 5, 2025

Lots of good feedback from @ramprasadagarwal. I'll hold while you guys sort it out, but I did notice that the screenshot of the drag and drop looks weird where the "drop area" appears behind the action buttons of the modal.

@nidhibhatg nidhibhatg force-pushed the nidhi_importer_filesystems branch from 811ebb9 to 3700a3c Compare May 20, 2025 09:54
@nidhibhatg nidhibhatg force-pushed the nidhi_importer_filesystems branch from 3700a3c to 1e68db6 Compare June 3, 2025 07:07
Copy link

github-actions bot commented Jun 3, 2025

UI Coverage Report

Lines Statements Branches Functions
Coverage: 32%
39.15% (30527/77959) 31.01% (14247/45936) 23.89% (2130/8915)

Copy link

github-actions bot commented Jun 3, 2025

Python Code Coverage

Python Coverage Report •
FileStmtsMissCoverMissing
TOTAL541852707850% 
report-only-changed-files is enabled. No files were changed during this commit :)

Pytest Report

Tests Skipped Failures Errors Time
1186 106 💤 0 ❌ 0 🔥 6m 2s ⏱️

Copy link
Collaborator

@bjornalm bjornalm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work. I reviewed the first half of the files. Will continue with the rest later, but you can start addressing the issues.

expect(screen.getByText('Select a source to import from')).toBeInTheDocument();
expect(screen.queryByText('LocalUpload')).toBeInTheDocument();
expect(screen.getByText('Amazon S3')).toBeInTheDocument();
expect(screen.getByText('HDFS')).toBeInTheDocument();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should cover all filesystems defined in ImporterSourceSelector.tsx


render(<ImporterSourceSelector setFileMetaData={mockSetFileMetaData} />);

const s3Button = screen.getAllByRole('button')[1]; // second button (first is local upload)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we are forced to use an index to access a button it is often a tell that we need to improve the accessibility of that button.

)}
{fileSystemsData?.map(filesystem => (
<div className="hue-importer__source-selector-option" key={filesystem.name}>
<Button
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either move the span title inside the button component or add aria-label={t(fileSystems[filesystem.name].title)} to the button


render(<ImporterSourceSelector setFileMetaData={mockSetFileMetaData} />);

expect(screen.getByText('Select a source to import from')).toBeInTheDocument();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this testing the loading state? The 'Select a source to import from' is shown after loading.

const errorConfig = [
{
enabled: !!error,
message: t('An error occurred while fetching the filesystem'),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we improve this message? Something about "one or more available filesystems". Can you check if we have any useful information from the API about why it failed?

const fileSize = file.size;
if (fileSize === 0) {
setUploadError(t('This file is empty, please select another file.'));
} else if (fileSize > 150 * 1024 * 1024) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of questions:

  1. The error msg states max is 200 KB, I assume it should be 150 MB and the error msg is wrong or? In that case this should be 150 * 1000 * 100 for MB (1024 is MiB)
  2. Where is this limit coming from? Is it available as a config value?

Please at least define this value in a const and reuse it in the error msg. There is a util function available to translate sizes into human readable form in a old vue file, I think we should extract that into a ts file, modify it to use Decimal base and start using it.

export const humanSize = (value?: number): string => {
  // Use SI decimal units for 1000-based calculations
  const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']; // Stays the same

  if (value === 0) {
    return '0 B';
  }
  if (!value) {
    return ''; // Or '0 B' depending on desired behavior for undefined/null
  }
  if (value < 1000) { // Changed here!
    return `${value} B`;
  }
  // Note! Changed base for calculations from 1024 to 1000
  const unitIndex = Math.min(Math.floor(Math.log(value) / Math.log(1000)), UNITS.length - 1); 
  const unitValue = Math.round((value / Math.pow(1000, unitIndex)) * 10) / 10; 
  return `${unitValue} ${UNITS[unitIndex]}`;
};

cc @ramprasadagarwal

} else if (fileSize > 150 * 1024 * 1024) {
setUploadError(
t(
'File size exceeds the supported size (200 KB). Please use the S3, ABFS or HDFS browser to upload files.'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we sending them off to a third party tool? Can't the user upload a larger file using the new storage browser in Hue? If so, lets create an actionable error msg that can take the user there.

const { t } = i18nReact.useTranslation();
const uploadRef = useRef<HTMLInputElement>(null);

const { save: upload } = useSaveData<LocalFileUploadResponse>(UPLOAD_LOCAL_FILE_API_URL);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To where are we actually uploading this file? The Hue web server? HDFS?
cc @ramprasadagarwal

data-testid="hue-file-input"
className="hue-importer__source-selector-option-upload"
onChange={handleFileUpload}
accept=".csv, .xlsx, .xls"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did we really agree that these are the only supported file formats?

);
});

it('should show error for file > 150mb', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: MB

@bjornalm
Copy link
Collaborator

bjornalm commented Jun 5, 2025

@nidhibhatg I'm also trying out some deep PR review AI prompting using the the copilot chat functionality. Please have a look, I think they make sense. Unfortunately I can't get them to appear as comments in the actual code.

  1. Accessibility: Table Row Interactivity
    File: FileChooserModal.tsx
    Location: Table row rendering and click handling

Comment:
For accessibility, consider adding role="button" and tabIndex={0} to clickable table rows, and handle keyboard interaction (Enter/Space) to allow keyboard users to select files or folders. This will make file/folder selection more accessible to all users.

  1. Accessibility: Error Message Announcements
    Files:

ImporterSourceSelector.tsx
FileChooserModal.tsx
LocalFileUploadOption.tsx
Comment:
To improve accessibility, error messages shown to users should be wrapped in a container with aria-live="polite" or aria-live="assertive" so that screen readers announce them as soon as they appear.

  1. Accessibility: Modal Focus Management
    File: FileChooserModal.tsx
    Location: Modal open/close logic

Comment:
Consider implementing focus management for modals: focus should be trapped within the modal while open, and returned to the triggering element when closed. This will improve keyboard navigation and accessibility for screen reader users.

  1. Error Handling: Backend Error Messages
    File: FileChooserModal.tsx
    Location: Folder creation error handling

Comment:
Currently, API errors (e.g., when creating a folder) are surfaced directly to users. Please consider mapping or sanitizing backend error messages into more user-friendly text to avoid exposing technical details or confusing messages to end users.

  1. Testing: Prefer user-event Over fireEvent
    Files:

LocalFileUploadOption.test.tsx
ImporterSourceSelector.test.tsx
BottomSlidePanel.test.tsx
Comment:
For future test maintenance, please prefer using @testing-library/user-event rather than fireEvent. user-event simulates real user interactions and helps catch more realistic issues.

  1. Minor: onPressEnter Handler Bug
    File: FileChooserModal.tsx
    Location: BottomSlidePanel for create folder

Comment:
The onPressEnter prop is currently set as onPressEnter={() => handleCreate}. This creates a new function that returns handleCreate instead of calling it. It should be onPressEnter={handleCreate} to ensure the handler is called when Enter is pressed.

@bjornalm bjornalm requested a review from Copilot June 5, 2025 14:45
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for configured filesystems in the importer and enhances the file chooser with upload and folder-creation capabilities.

  • Introduces a reusable BottomSlidePanel component for folder creation UI
  • Extends FileChooserModal with drag-and-drop, polling, upload button, and error/retry handling
  • Updates ImporterSourceSelector to fetch and render configured filesystems dynamically

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

File Description
reactComponents/BottomSlidePanel/BottomSlidePanel.tsx New bottom slide panel component
apps/storageBrowser/FileChooserModal/FileChooserModal.tsx File chooser extended to support upload & folder creation
apps/newimporter/ImporterSourceSelector/ImporterSourceSelector.tsx Fetch and display configured filesystems
apps/newimporter/ImporterSourceSelector/LocalFileUploadOption.tsx Local file upload option with size validation
Comments suppressed due to low confidence (4)

desktop/core/src/desktop/js/reactComponents/BottomSlidePanel/BottomSlidePanel.tsx:24

  • The onPrimaryClick prop is typed as (unknown) => void, which is ambiguous. Consider changing it to a clear signature like () => void or specify the exact argument type.
onPrimaryClick?: (unknown) => void;

desktop/core/src/desktop/js/reactComponents/BottomSlidePanel/BottomSlidePanel.tsx:66

  • The mask div has role="button" but no accessible name. Add aria-label or aria-labelledby (e.g., aria-label="Close panel") so screen readers announce its purpose.
<div className={`hue-bottom-slide-mask ${isOpen ? 'fade-in' : 'fade-out'}`}

desktop/core/src/desktop/js/reactComponents/BottomSlidePanel/BottomSlidePanel.scss:62

  • Missing semicolon after the padding: 16px 24px property in .hue-bottom-slide-panel__footer. Add a semicolon to avoid CSS parsing errors.
    padding: 16px 24px

desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx:207

  • The variable name is undefined here. You probably meant to use createFolderValue or the new folder name returned from the API.
          setDestPath(prev => `${prev}/${name}`);

<Input
defaultValue={createFolderValue}
disabled={createFolderLoading}
onPressEnter={() => handleCreate}
Copy link
Preview

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handler () => handleCreate returns the function instead of invoking it. Change this to onPressEnter={handleCreate} or onPressEnter={() => handleCreate()}.

Suggested change
onPressEnter={() => handleCreate}
onPressEnter={() => handleCreate()}

Copilot uses AI. Check for mistakes.

} else if (fileSize > 150 * 1024 * 1024) {
setUploadError(
t(
'File size exceeds the supported size (200 KB). Please use the S3, ABFS or HDFS browser to upload files.'
Copy link
Preview

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message references "200 KB" but the code checks fileSize > 150 * 1024 * 1024 (~150 MB). Either update the threshold or correct the message to match.

Suggested change
'File size exceeds the supported size (200 KB). Please use the S3, ABFS or HDFS browser to upload files.'
'File size exceeds the supported size (150 MB). Please use the S3, ABFS or HDFS browser to upload files.'

Copilot uses AI. Check for mistakes.

Comment on lines +116 to +130
{fileSystemsData?.map(filesystem => (
<div className="hue-importer__source-selector-option" key={filesystem.name}>
<Button
className="hue-importer__source-selector-option-button"
size="large"
icon={fileSystems[filesystem.name].icon}
onClick={() => {
setSelectedUserHomeDirectory(filesystem.userHomeDirectory);
}}
></Button>
<span className="hue-importer__source-selector-option-btn-title">
{t(fileSystems[filesystem.name].title)}
</span>
</div>
))}
Copy link
Preview

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indexing fileSystems by filesystem.name without checking if the key exists may throw at runtime. Consider filtering unsupported names or providing a fallback icon.

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants