Skip to content

Conversation

@jr-rk
Copy link

@jr-rk jr-rk commented Jul 4, 2025

Phases MP MM MB MR JM Total
ETA 0 0 0 0 0 0
Developing 0 0 0 0 0 0
Review 0 0 0 0 0 0
Total - - - - - 0
ETA est. 0
ETA cust. - - - - - 0

Problem description

Items are prefetching their previews even without "preview" button clicked, it is unnecessary.

Reported issues

ufal#1212

Summary by CodeRabbit

  • New Features

    • Preview data for non-video files is now loaded only when the user clicks the "Show Preview" button, improving performance and reducing unnecessary data usage.
    • The preview section now accepts an externally provided list of files, enhancing flexibility in file management.
    • Introduced a new service to fetch and process sub-file data within archives or directories, enabling detailed file inspection.
  • Improvements

    • Added loading indicators and better state tracking for preview visibility and loading status, providing clearer feedback during preview loading.
    • Updated preview section to handle file lists synchronously, simplifying the display logic and improving responsiveness.

@jr-rk jr-rk self-assigned this Jul 4, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 4, 2025

Walkthrough

The changes implement deferred loading of preview data for non-video files in the file description component. Preview data is now fetched only when the user clicks the preview button, rather than immediately on initialization. New state flags and methods manage the preview's visibility and loading state, with immediate loading retained for video files. Additionally, the preview section component was refactored to receive the list of files as an input rather than fetching it internally, and related template adjustments were made accordingly. A new service was introduced to fetch sub-file data for bitstreams, and a corresponding interface was added to model the sub-file API response.

Changes

File(s) Change Summary
src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html Added a click event handler to the preview button to trigger preview loading while retaining existing behavior.
src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts Refactored to load preview data on demand for non-video files; added state flags and new methods for control.
src/app/item-page/clarin-files-section/clarin-files-section.component.html Added [listOfFiles] input binding to <ds-preview-section> to pass the resolved list of files.
src/app/item-page/simple/field-components/preview-section/preview-section.component.html Updated template to treat listOfFiles as a synchronous array instead of an observable; removed async pipe usage.
src/app/item-page/simple/field-components/preview-section/preview-section.component.ts Changed listOfFiles from internally fetched observable to an external @Input() property; removed RegistryService dependency and fetching logic.
src/app/core/data/sub-file-data.service.ts Added new service to fetch and process sub-file data related to a bitstream via HTTP requests and endpoint discovery.
src/app/core/metadata/subfile-data.model.ts Added interface modeling the raw API response structure for sub-files within archives or directories.
src/app/item-page/simple/field-components/preview-section/preview-section.component.spec.ts Simplified test for listOfFiles initialization by removing asynchronous subscription and done callback.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FileDescriptionComponent
    participant DataService

    User->>FileDescriptionComponent: Clicks "Show Preview" (non-video)
    FileDescriptionComponent->>FileDescriptionComponent: set isLoadingPreview = true, isPreviewVisible = true
    FileDescriptionComponent->>DataService: loadPreviewData()
    DataService-->>FileDescriptionComponent: Returns content URL & thumbnail
    FileDescriptionComponent->>FileDescriptionComponent: set isLoadingPreview = false, update observables
Loading

Possibly related PRs

  • Ufal dtq sync 2025 05 14 #855: Adds user-triggered preview loading and state flags in FileDescriptionComponent, focusing on secure video preview with token renewal and observable URL handling.

Suggested reviewers

  • milanmajchrak
  • vidiecan

Poem

A click, a hop, the preview’s here,
No need to load when none is near.
For videos, quick as a bunny’s run,
For others, wait—then comes the fun!
With flags and flow so neat and new,
The rabbit cheers this code review! 🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

yarn install v1.22.22
[1/4] Resolving packages...
(node:17896) [DEP0169] DeprecationWarning: url.parse() behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for url.parse() vulnerabilities.
(Use node --trace-deprecation ... to show where the warning was created)
[2/4] Fetching packages...
error [email protected]: The engine "node" is incompatible with this module. Expected version "^14 || ^16 || ^17 || ^18 || ^19". Got "24.3.0"
error Found incompatible module.
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1df9c2 and 0cfb871.

📒 Files selected for processing (1)
  • src/app/item-page/simple/field-components/preview-section/preview-section.component.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/item-page/simple/field-components/preview-section/preview-section.component.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: dspace-angular / docker-build (linux/amd64, ubuntu-latest, true)
  • GitHub Check: tests (18.x)
  • GitHub Check: tests (16.x)
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@jr-rk jr-rk requested a review from Copilot July 4, 2025 09:18
@jr-rk
Copy link
Author

jr-rk commented Jul 4, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jul 4, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

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 defers fetching of non-video file previews until the user clicks "Show Preview," while preserving immediate loading for videos.

  • Introduces isPreviewVisible and isLoadingPreview flags to manage preview display and loading state.
  • Extracts preview logic into a new loadPreviewData method and adds showPreview and isVideo helpers.
  • Updates template to trigger showPreview() on click rather than prefetching.

Reviewed Changes

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

File Description
file-description.component.ts Refactored preview loading logic into loadPreviewData, added flags and click handler methods
file-description.component.html Added (click)="showPreview()" to preview button to defer data loading
Comments suppressed due to low confidence (1)

src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts:110

  • Add unit tests for showPreview() to verify that isLoadingPreview and isPreviewVisible are set correctly and that loadPreviewData() is invoked.
  public showPreview(): void {

Comment on lines 49 to 53
<a class="preview-btn collapsed" data-toggle="collapse" *ngIf="couldPreview()" role="button"
href="#file_file_{{ fileInput.id }}">
(click)="showPreview()" href="#file_file_{{ fileInput.id }}">
<i class="fa fa-eye">&nbsp;</i>
{{'item.file.description.preview' | translate}}
</a>
Copy link

Copilot AI Jul 4, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider using a element or adding $event.preventDefault() in the click handler to avoid default anchor navigation and improve accessibility.

Copilot uses AI. Check for mistakes.
private loadPreviewData(): void {
this.content_url$ = this.bitstreamService.findById(this.fileInput.id, true, false, followLink('thumbnail'))
.pipe(getFirstCompletedRemoteData(),
switchMap((remoteData: RemoteData<Bitstream>) => {
Copy link

Copilot AI Jul 4, 2025

Choose a reason for hiding this comment

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

The switchMap callback returns an Observable only when hasSucceeded is true; add a default return of('') (or similar) for the failure path to avoid emitting undefined.

Copilot uses AI. Check for mistakes.
* It sets flags to show the preview section and a loading indicator,
* then starts fetching the required data.
*/
public showPreview(): void {
Copy link

Copilot AI Jul 4, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider guarding against repeated clicks (e.g., disable the preview button while isLoadingPreview is true) to prevent multiple concurrent data fetches.

Suggested change
public showPreview(): void {
public showPreview(): void {
if (this.isLoadingPreview) {
return; // Prevent multiple concurrent calls
}

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76e5772 and e9fec39.

📒 Files selected for processing (2)
  • src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html (1 hunks)
  • src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts (1)
src/app/core/shared/operators.ts (1)
  • getFirstCompletedRemoteData (41-43)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: dspace-angular / docker-build (linux/amd64, ubuntu-latest, true)
  • GitHub Check: tests (16.x)
  • GitHub Check: tests (18.x)
🔇 Additional comments (5)
src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts (4)

41-48: LGTM! Well-documented state properties.

The boolean flags are properly declared with clear documentation explaining their purpose for managing preview visibility and loading state.


66-71: LGTM! Proper conditional loading logic.

The conditional logic correctly implements the deferred loading requirement - video files load immediately while other file types wait for user interaction.


105-114: LGTM! Clean method implementation.

The showPreview() method correctly sets the loading state and triggers data loading when the user clicks the preview button.


116-121: LGTM! Simple and clear helper method.

The isVideo() helper method provides a clean way to determine if the file is a video type.

src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html (1)

50-50: LGTM! Proper integration of click handler.

The click handler is correctly added to trigger the new preview loading logic while preserving the existing href attribute for Bootstrap collapse functionality. This ensures both the new deferred loading behavior and the existing UI collapse behavior work together seamlessly.

Comment on lines +127 to +154
private loadPreviewData(): void {
this.content_url$ = this.bitstreamService.findById(this.fileInput.id, true, false, followLink('thumbnail'))
.pipe(getFirstCompletedRemoteData(),
switchMap((remoteData: RemoteData<Bitstream>) => {
// Hide loading indicator once the request is complete
this.isLoadingPreview = false;
if (remoteData.hasSucceeded) {
if (remoteData.payload?.thumbnail){
this.thumbnail_url$ = remoteData.payload?.thumbnail.pipe(
switchMap((thumbnailRD: RemoteData<Bitstream>) => {
if (thumbnailRD.hasSucceeded) {
return this.buildUrl(thumbnailRD.payload?._links.content.href);
} else {
return of('');
}
}),
);
} else {
this.thumbnail_url$ = of('');
}
return of(remoteData.payload?._links.content.href);
}
}
));
this.content_url$.pipe(take(1)).subscribe((url) => {
this.content_url = url;
});
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address subscription cleanup and error handling concerns.

The method has several issues that need attention:

  1. Memory leak risk: The subscription at line 151-153 is not added to the subscriptions collection for cleanup
  2. Incomplete error handling: The loading flag is only set to false in the success path, potentially leaving it stuck if errors occur
  3. High complexity: The nested observables make the method difficult to maintain

Apply this diff to fix the subscription cleanup:

-    this.content_url$.pipe(take(1)).subscribe((url) => {
-      this.content_url = url;
-    });
+    this.subscriptions.add(
+      this.content_url$.pipe(take(1)).subscribe((url) => {
+        this.content_url = url;
+      })
+    );

Consider refactoring to improve error handling:

   private loadPreviewData(): void {
     this.content_url$ = this.bitstreamService.findById(this.fileInput.id, true, false, followLink('thumbnail'))
       .pipe(getFirstCompletedRemoteData(),
             switchMap((remoteData: RemoteData<Bitstream>) => {
-              // Hide loading indicator once the request is complete
-              this.isLoadingPreview = false;
               if (remoteData.hasSucceeded) {
                 if (remoteData.payload?.thumbnail){
                   this.thumbnail_url$ = remoteData.payload?.thumbnail.pipe(
                     switchMap((thumbnailRD: RemoteData<Bitstream>) => {
                       if (thumbnailRD.hasSucceeded) {
                         return this.buildUrl(thumbnailRD.payload?._links.content.href);
                       } else {
                         return of('');
                       }
                     }),
                   );
                 } else {
                   this.thumbnail_url$ = of('');
                 }
                 return of(remoteData.payload?._links.content.href);
               }
+              return of('');
             }
       ));
     this.subscriptions.add(
       this.content_url$.pipe(take(1)).subscribe((url) => {
         this.content_url = url;
+        // Hide loading indicator once the request is complete
+        this.isLoadingPreview = false;
       })
     );
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private loadPreviewData(): void {
this.content_url$ = this.bitstreamService.findById(this.fileInput.id, true, false, followLink('thumbnail'))
.pipe(getFirstCompletedRemoteData(),
switchMap((remoteData: RemoteData<Bitstream>) => {
// Hide loading indicator once the request is complete
this.isLoadingPreview = false;
if (remoteData.hasSucceeded) {
if (remoteData.payload?.thumbnail){
this.thumbnail_url$ = remoteData.payload?.thumbnail.pipe(
switchMap((thumbnailRD: RemoteData<Bitstream>) => {
if (thumbnailRD.hasSucceeded) {
return this.buildUrl(thumbnailRD.payload?._links.content.href);
} else {
return of('');
}
}),
);
} else {
this.thumbnail_url$ = of('');
}
return of(remoteData.payload?._links.content.href);
}
}
));
this.content_url$.pipe(take(1)).subscribe((url) => {
this.content_url = url;
});
}
private loadPreviewData(): void {
this.content_url$ = this.bitstreamService.findById(this.fileInput.id, true, false, followLink('thumbnail'))
.pipe(
getFirstCompletedRemoteData(),
switchMap((remoteData: RemoteData<Bitstream>) => {
if (remoteData.hasSucceeded) {
if (remoteData.payload?.thumbnail) {
this.thumbnail_url$ = remoteData.payload?.thumbnail.pipe(
switchMap((thumbnailRD: RemoteData<Bitstream>) => {
if (thumbnailRD.hasSucceeded) {
return this.buildUrl(thumbnailRD.payload?._links.content.href);
} else {
return of('');
}
}),
);
} else {
this.thumbnail_url$ = of('');
}
return of(remoteData.payload?._links.content.href);
}
return of('');
})
);
this.subscriptions.add(
this.content_url$.pipe(take(1)).subscribe((url) => {
this.content_url = url;
// Hide loading indicator once the request is complete
this.isLoadingPreview = false;
})
);
}
🤖 Prompt for AI Agents
In
src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts
lines 127 to 154, fix the memory leak by adding the subscription from
content_url$ to the component's subscriptions collection for proper cleanup.
Also, ensure the isLoadingPreview flag is set to false in both success and error
paths by handling errors in the observable chain. Refactor the nested switchMap
calls to flatten the observable structure, improving readability and
maintainability.

@jr-rk
Copy link
Author

jr-rk commented Jul 7, 2025

byHandle request is still called, the real problem is in clarin-files-section.component and preview-section.component - there it is called via registryService attr. Both request calls are very similar, isn't it redundant? Also, loading the component is dependent on some data from the response - are backend changes needed too?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/app/core/data/sub-file-data.service.ts (1)

9-65: Add unit tests for the new service.

This new service lacks unit tests, which are essential for ensuring reliability and maintainability.

The service should have comprehensive unit tests covering:

  • Successful sub-file fetching
  • Error handling scenarios
  • Data conversion edge cases
  • Endpoint URL construction

Would you like me to generate unit test code for this service?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b20a51 and f1df9c2.

📒 Files selected for processing (3)
  • src/app/core/data/sub-file-data.service.ts (1 hunks)
  • src/app/core/metadata/subfile-data.model.ts (1 hunks)
  • src/app/item-page/simple/field-components/preview-section/preview-section.component.spec.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/app/core/metadata/subfile-data.model.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: dspace-angular / docker-build (linux/amd64, ubuntu-latest, true)
  • GitHub Check: tests (18.x)
  • GitHub Check: tests (16.x)
🔇 Additional comments (1)
src/app/core/data/sub-file-data.service.ts (1)

34-34: Please confirm the ‘subfiles’ HAL link exists and document it
Before shipping, ensure that your backend actually exposes a HAL link named subfiles. To make this explicit and easier to maintain:

  • Extract 'subfiles' into a well-named constant (e.g. SUBFILES_LINK = 'subfiles') at the top of src/app/core/data/sub-file-data.service.ts
  • Add a brief comment or point to the backend API spec where this link is defined

Location to update:

  • src/app/core/data/sub-file-data.service.ts, method getSubFileEndpoint() (around line 34)

Comment on lines 82 to 84
it('should set listOfFiles on init', () => {
expect(component.listOfFiles).toEqual([]);
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Initialize the input property in the test setup.

Since listOfFiles is now an @Input() property (as mentioned in the AI summary), this test assertion will fail because the input property is not initialized. Add the input property initialization in the test setup.

  beforeEach(() => {
    fixture = TestBed.createComponent(PreviewSectionComponent);
    component = fixture.componentInstance;
    
+   // Initialize the input property
+   component.listOfFiles = [];
+
    // Set up the mock service's getMetadataBitstream method to return a simple stream
🤖 Prompt for AI Agents
In
src/app/item-page/simple/field-components/preview-section/preview-section.component.spec.ts
around lines 82 to 84, the test checks the value of the @Input() property
listOfFiles without initializing it, causing the test to fail. Fix this by
setting the listOfFiles input property to an empty array in the test setup
before running the assertion, ensuring the component input is properly
initialized.

Comment on lines 47 to 64
private convertToFileInfo(data: SubFileResponse): FileInfo[] {
const result: FileInfo[] = [];

Object.keys(data).forEach(key => {
const subFileData = data[key];
const fileInfo = new FileInfo();

// Map properties from the API response to FileInfo
fileInfo.name = subFileData.name || key;
fileInfo.content = subFileData.content;
fileInfo.size = subFileData.size;
fileInfo.isDirectory = !!subFileData.isDirectory;

result.push(fileInfo);
});

return result;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling and null safety in data conversion.

The conversion method should handle potential null/undefined values more robustly to prevent runtime errors.

  private convertToFileInfo(data: SubFileResponse): FileInfo[] {
    const result: FileInfo[] = [];
+   
+   if (!data || typeof data !== 'object') {
+     return result;
+   }

    Object.keys(data).forEach(key => {
      const subFileData = data[key];
+     if (!subFileData) {
+       return; // Skip null/undefined entries
+     }
+     
      const fileInfo = new FileInfo();

      // Map properties from the API response to FileInfo
      fileInfo.name = subFileData.name || key;
      fileInfo.content = subFileData.content;
-     fileInfo.size = subFileData.size;
+     fileInfo.size = subFileData.size || 0;
      fileInfo.isDirectory = !!subFileData.isDirectory;

      result.push(fileInfo);
    });

    return result;
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private convertToFileInfo(data: SubFileResponse): FileInfo[] {
const result: FileInfo[] = [];
Object.keys(data).forEach(key => {
const subFileData = data[key];
const fileInfo = new FileInfo();
// Map properties from the API response to FileInfo
fileInfo.name = subFileData.name || key;
fileInfo.content = subFileData.content;
fileInfo.size = subFileData.size;
fileInfo.isDirectory = !!subFileData.isDirectory;
result.push(fileInfo);
});
return result;
}
private convertToFileInfo(data: SubFileResponse): FileInfo[] {
const result: FileInfo[] = [];
if (!data || typeof data !== 'object') {
return result;
}
Object.keys(data).forEach(key => {
const subFileData = data[key];
if (!subFileData) {
return; // Skip null/undefined entries
}
const fileInfo = new FileInfo();
// Map properties from the API response to FileInfo
fileInfo.name = subFileData.name || key;
fileInfo.content = subFileData.content;
fileInfo.size = subFileData.size || 0;
fileInfo.isDirectory = !!subFileData.isDirectory;
result.push(fileInfo);
});
return result;
}
🤖 Prompt for AI Agents
In src/app/core/data/sub-file-data.service.ts around lines 47 to 64, the
convertToFileInfo method lacks checks for null or undefined values in the input
data and its properties, which can cause runtime errors. Add null and undefined
checks before accessing properties of subFileData and ensure data itself is
valid before processing. Use safe access patterns or conditional checks to
handle missing or malformed data gracefully, returning an empty array or
skipping invalid entries as appropriate.

Comment on lines 21 to 27
fetchSubFiles(bitstreamId: string): Observable<FileInfo[]> {
return this.getSubFileEndpoint().pipe(
map((endpoint) => this.getSubFilesRequestURL(endpoint, bitstreamId)),
mergeMap((requestUrl) => this.http.get<SubFileResponse>(requestUrl)),
map((data: SubFileResponse) => this.convertToFileInfo(data))
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling to the main service method.

The service should handle potential errors in the observable chain to provide better error handling for consuming components.

+import { catchError } from 'rxjs/operators';
+import { throwError } from 'rxjs';

  fetchSubFiles(bitstreamId: string): Observable<FileInfo[]> {
    return this.getSubFileEndpoint().pipe(
      map((endpoint) => this.getSubFilesRequestURL(endpoint, bitstreamId)),
      mergeMap((requestUrl) => this.http.get<SubFileResponse>(requestUrl)),
-     map((data: SubFileResponse) => this.convertToFileInfo(data))
+     map((data: SubFileResponse) => this.convertToFileInfo(data)),
+     catchError((error) => {
+       console.error('Failed to fetch sub-files:', error);
+       return throwError(error);
+     })
    );
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fetchSubFiles(bitstreamId: string): Observable<FileInfo[]> {
return this.getSubFileEndpoint().pipe(
map((endpoint) => this.getSubFilesRequestURL(endpoint, bitstreamId)),
mergeMap((requestUrl) => this.http.get<SubFileResponse>(requestUrl)),
map((data: SubFileResponse) => this.convertToFileInfo(data))
);
}
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
fetchSubFiles(bitstreamId: string): Observable<FileInfo[]> {
return this.getSubFileEndpoint().pipe(
map((endpoint) => this.getSubFilesRequestURL(endpoint, bitstreamId)),
mergeMap((requestUrl) => this.http.get<SubFileResponse>(requestUrl)),
map((data: SubFileResponse) => this.convertToFileInfo(data)),
catchError((error) => {
console.error('Failed to fetch sub-files:', error);
return throwError(error);
})
);
}
🤖 Prompt for AI Agents
In src/app/core/data/sub-file-data.service.ts around lines 21 to 27, the
fetchSubFiles method lacks error handling in its observable chain. Add an error
handling operator such as catchError to the observable pipeline to catch and
handle any errors that occur during the HTTP request or data processing. This
should return a user-friendly error or fallback value and ensure the observable
does not fail silently, improving robustness for consuming components.

@jr-rk jr-rk linked an issue Aug 25, 2025 that may be closed by this pull request
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.

UFAL/Don't prefetch preview data unnecessarily

2 participants