Skip to content

[NAE-2416] AbstractFileDefaultFieldComponent does not push upload event#326

Open
renczesstefan wants to merge 1 commit intorelease/7.0.0-rev10from
NAE-2416
Open

[NAE-2416] AbstractFileDefaultFieldComponent does not push upload event#326
renczesstefan wants to merge 1 commit intorelease/7.0.0-rev10from
NAE-2416

Conversation

@renczesstefan
Copy link
Copy Markdown
Member

@renczesstefan renczesstefan commented Apr 24, 2026

Description

Implements NAE-2416

Dependencies

No new dependencies were introduced.

Third party dependencies

No new dependencies were introduced.

Blocking Pull requests

There are no dependencies on other PR.

How Has Been This Tested?

This was tested manually and with unit tests.

Test Configuration

Name Tested on
OS macOS Tahoe 26.3
Runtime Node 20.17.0
Dependency Manager NPM 10.8.2
Framework version Angular 13.3.1
Run parameters
Other configuration

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My changes have been checked, personally or remotely, with @machacjozef
  • I have commented my code, particularly in hard-to-understand areas
  • I have resolved all conflicts with the target branch of the PR
  • I have updated and synced my code with the target branch
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing tests pass locally with my changes:
    • Lint test
    • Unit tests
    • Integration tests
  • I have checked my contribution with code analysis tools:
  • I have made corresponding changes to the documentation:
    • Developer documentation
    • User Guides
    • Migration Guides

Summary by CodeRabbit

  • New Features

    • File uploads now support executing actions returned from the server.
    • Added support for snackbar actions as a registered action type.
  • Bug Fixes

    • Improved error handling in file upload flows with better error messaging and state management.

…nt notification to TaskEventService

- Integrated `FrontActionService` in `AbstractFileDefaultFieldComponent` to handle front-end actions based on outcomes.
- Enhanced file upload logic with better error handling and front action triggers, such as displaying success feedback via snackbar actions.
- Updated tests for `AbstractFileListDefaultFieldComponent` to include `FrontActionService`.
- Registered a new `snackBar` front action in the `FrontActionModule`.
- Refactored the file upload methods to utilize modern RXJS subscription patterns with error handling improvements.
- Improved readability and maintainability of file download and preview logic by addressing structuring and formatting issues.
- Removed unused imports and simplified test components.

These changes aim to improve the user experience by handling outcome-based front actions and offering better feedback on file operations.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Walkthrough

File upload components now integrate FrontActionService to execute frontend actions parsed from upload responses. The FrontActionModule registers the snackBar action, and upload handlers refactor to use object-based subscription syntax with centralized error handling while parsing and executing frontActions when available.

Changes

Cohort / File(s) Summary
Front Action Module Registration
projects/netgrif-components-core/src/lib/actions/front-action.module.ts
Added snackBarAction import and registration under action key 'snackBar' in the module constructor.
File Default Field Components
projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts, projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.spec.ts, projects/netgrif-components/src/lib/data-fields/file-field/file-default-field/file-default-field.component.ts
Injected FrontActionService into component constructors. Upload flow refactored to object-based subscription with explicit error handling, now parses and executes frontActions from responses via service. Removed unused snack-bar test imports.
File List Field Components
projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.ts, projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.spec.ts, projects/netgrif-components/src/lib/data-fields/file-list-field/file-list-default-field/file-list-default-field.component.ts
Added FrontActionService injection. Refactored upload request handling from callback to object-based { next, error } structure. Success path now parses and executes frontend actions; error path centralized with fallback error translations. Cleaned up unused test imports.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FileComponent as File Upload Component
    participant FrontActionService
    participant Backend API
    participant SnackBar
    
    User->>FileComponent: Upload file
    FileComponent->>Backend API: POST file
    Backend API-->>FileComponent: Return response with frontActions
    FileComponent->>FileComponent: Parse frontActions from response
    alt Has frontActions
        FileComponent->>FrontActionService: Execute parsed frontActions
        FrontActionService->>SnackBar: Trigger snackBar action (if applicable)
        SnackBar-->>User: Display notification
    else No frontActions
        FileComponent->>SnackBar: Show success snackbar
        SnackBar-->>User: Display success message
    end
    
    alt Response has error
        FileComponent->>SnackBar: Show error snackbar
        SnackBar-->>User: Display error message
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #305: Adds FrontActionService injection and execution of parsed frontActions to task event service outcome-handling flows.
  • #324: Modifies the same file upload component files with constructor signature and upload flow alterations.

Suggested labels

bugfix, improvement, Medium

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly matches the core objective: integrating FrontActionService to handle front actions (particularly snackbar) in file upload operations, addressing the issue of missing upload event handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added improvement New feature or request bugfix Medium labels Apr 24, 2026
Copy link
Copy Markdown

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.spec.ts (1)

93-102: 🧹 Nitpick | 🔵 Trivial

Minor: align local parameter order with the base class.

Local params declare sanitizer before eventService, but the parent class expects them in the reverse order (and the super(...) call correctly passes them in parent order by name). The code works, but mirroring the parent's parameter order in this test subclass avoids confusion and matches the FileDefaultFieldComponent conventions.

♻️ Proposed reorder
 class TestFileComponent extends AbstractFileDefaultFieldComponent {
     constructor(taskResourceService: TaskResourceService,
                 log: LoggerService,
                 snackbar: SnackBarService,
                 translate: TranslateService,
-                sanitizer: DomSanitizer,
                 eventService: EventService,
+                sanitizer: DomSanitizer,
                 frontActionService: FrontActionService,
                 `@Optional`() `@Inject`(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<FileField>) {
         super(taskResourceService, log, snackbar, translate, eventService, sanitizer, frontActionService, dataFieldPortalData);
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.spec.ts`
around lines 93 - 102, The constructor parameter order in
AbstractFileDefaultFieldComponentSpec is inconsistent with the base class: swap
the local parameters so eventService comes before sanitizer to mirror the parent
class signature; update the constructor parameter list (constructor(...
eventService: EventService, sanitizer: DomSanitizer ...)) while leaving the
super(...) call unchanged so the parameter order matches
FileDefaultFieldComponent and avoids confusion.
projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts (1)

113-125: ⚠️ Potential issue | 🟠 Major

Breaking change in public constructor signature requires migration documentation.

AbstractFileDefaultFieldComponent is exported from @netgrif/components-core as public API. The constructor signature changed by inserting _frontActionService in the middle of the parameter list (after _sanitizer, before dataFieldPortalData). External subclasses not recompiled against this version will receive the wrong argument at the dataFieldPortalData position and fail at runtime.

Internal subclasses (FileDefaultFieldComponent, TestFileComponent) have been updated correctly. However, the CHANGELOG contains no documentation of this breaking change, and no migration guide has been created. Consider either:

  • Appending _frontActionService at the end (before @Optional() dataFieldPortalData)
  • Marking it @Optional() to retain backward compatibility

At minimum, document this breaking change in the next release notes for consumers updating from earlier versions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts`
around lines 113 - 125, The public constructor of
AbstractFileDefaultFieldComponent was changed by inserting _frontActionService
before dataFieldPortalData which breaks external subclasses; to fix, restore
backward compatibility by making the new parameter optional or moving it to the
end: either annotate the _frontActionService parameter with `@Optional`() (so
existing callers still pass DATA_FIELD_PORTAL_DATA into dataFieldPortalData) or
append _frontActionService after the existing parameters but before
dataFieldPortalData, ensuring DATA_FIELD_PORTAL_DATA (dataFieldPortalData) stays
last; update the constructor signature in AbstractFileDefaultFieldComponent and
keep references to _frontActionService, DATA_FIELD_PORTAL_DATA and
dataFieldPortalData consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts`:
- Around line 234-236: The failure log is printing the File object instead of
its name; update the calls to this._log.error in the upload-failure paths (the
one using this.dataField.stringId and
this.fileUploadEl.nativeElement.files.item(0) and the similar block around lines
273-275) to reference the file's name property (use
this.fileUploadEl.nativeElement.files.item(0).name) so the log matches the
success-path format and operators see the actual filename; keep the same
message/context and still pass response.error as the second argument to
this._log.error.
- Around line 232-241: The outer if checks response.error and the inner if
(response.error) is dead code; remove the nested check and ensure the snackbar
fallback is reachable by using a single conditional: inside the existing block
where response.error is truthy call this._log.error(...) with response.error and
then call
this._snackbar.openErrorSnackBar(this._translate.instant(response.error ||
'dataField.snackBar.fileUploadFailed')); update references to response.error,
this._log.error, this._snackbar.openErrorSnackBar and this._translate to
implement this single check (keep fileUploadEl/nativeElement and
this.dataField.stringId unchanged).
- Around line 258-261: FrontActionService.run currently logs an error when an
action ID is not registered but does not return, causing fn to be undefined and
a TypeError when fn.call(...) is invoked; update FrontActionService.run so that
inside the if-block that checks for missing action (the block that logs the
error) you add an early return to exit the method and avoid calling fn.call,
ensuring runAll() and subscribers won’t throw when
parseFrontActionsFromOutcomeTree yields unknown IDs.

In
`@projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.ts`:
- Around line 178-183: The debug message "Files [...] were successfully
uploaded" is logged unconditionally on non-progress responses, causing false
success logs; move the _log.debug(...) call into the success branch so it only
runs when response.error is falsy—specifically place it after the code that sets
this.state.error = false (the success handling block in the poll/response
handler inside abstract-file-list-default-field component), so that only
successful responses trigger the debug log and error responses do not.

---

Outside diff comments:
In
`@projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.spec.ts`:
- Around line 93-102: The constructor parameter order in
AbstractFileDefaultFieldComponentSpec is inconsistent with the base class: swap
the local parameters so eventService comes before sanitizer to mirror the parent
class signature; update the constructor parameter list (constructor(...
eventService: EventService, sanitizer: DomSanitizer ...)) while leaving the
super(...) call unchanged so the parameter order matches
FileDefaultFieldComponent and avoids confusion.

In
`@projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts`:
- Around line 113-125: The public constructor of
AbstractFileDefaultFieldComponent was changed by inserting _frontActionService
before dataFieldPortalData which breaks external subclasses; to fix, restore
backward compatibility by making the new parameter optional or moving it to the
end: either annotate the _frontActionService parameter with `@Optional`() (so
existing callers still pass DATA_FIELD_PORTAL_DATA into dataFieldPortalData) or
append _frontActionService after the existing parameters but before
dataFieldPortalData, ensuring DATA_FIELD_PORTAL_DATA (dataFieldPortalData) stays
last; update the constructor signature in AbstractFileDefaultFieldComponent and
keep references to _frontActionService, DATA_FIELD_PORTAL_DATA and
dataFieldPortalData consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 577ae8e8-25c9-4e4c-9839-9902477a95e4

📥 Commits

Reviewing files that changed from the base of the PR and between 8c5cf10 and c3755d2.

📒 Files selected for processing (7)
  • projects/netgrif-components-core/src/lib/actions/front-action.module.ts
  • projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.spec.ts
  • projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts
  • projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.spec.ts
  • projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.ts
  • projects/netgrif-components/src/lib/data-fields/file-field/file-default-field/file-default-field.component.ts
  • projects/netgrif-components/src/lib/data-fields/file-list-field/file-list-default-field/file-list-default-field.component.ts

Comment on lines 232 to +241
if (response.error) {
this._snackbar.openErrorSnackBar(this._translate.instant(response.error));
this.state.error = true;
this._log.error(
`File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, response.error
);
if (response.error) {
this._snackbar.openErrorSnackBar(this._translate.instant(response.error));
} else {
this._snackbar.openErrorSnackBar(this._translate.instant('dataField.snackBar.fileUploadFailed'));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Dead code: inner if (response.error) is always true.

Line 232 already guards response.error being truthy, so the nested if (response.error) on line 237 cannot be false and the else fallback to dataField.snackBar.fileUploadFailed is unreachable. Either drop the inner branch or replace the outer guard with a different condition (e.g. check for a truthy outcome/response shape) so the fallback is actually reachable.

🛠️ Proposed fix
                         if (response.error) {
                             this.state.error = true;
                             this._log.error(
                                 `File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, response.error
                             );
-                            if (response.error) {
-                                this._snackbar.openErrorSnackBar(this._translate.instant(response.error));
-                            } else {
-                                this._snackbar.openErrorSnackBar(this._translate.instant('dataField.snackBar.fileUploadFailed'));
-                            }
+                            this._snackbar.openErrorSnackBar(this._translate.instant(response.error));
                         } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts`
around lines 232 - 241, The outer if checks response.error and the inner if
(response.error) is dead code; remove the nested check and ensure the snackbar
fallback is reachable by using a single conditional: inside the existing block
where response.error is truthy call this._log.error(...) with response.error and
then call
this._snackbar.openErrorSnackBar(this._translate.instant(response.error ||
'dataField.snackBar.fileUploadFailed')); update references to response.error,
this._log.error, this._snackbar.openErrorSnackBar and this._translate to
implement this single check (keep fileUploadEl/nativeElement and
this.dataField.stringId unchanged).

Comment on lines +234 to +236
this._log.error(
`File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, response.error
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Error logs print File object instead of file name.

this.fileUploadEl.nativeElement.files.item(0) returns a File reference that renders as [object File] in most log sinks; the success-path log at line 246 correctly uses .name. Make the failure logs consistent so operators can identify which file failed.

🛠️ Proposed fix
-                            this._log.error(
-                                `File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, response.error
-                            );
+                            this._log.error(
+                                `File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)?.name} uploading has failed!`, response.error
+                            );
-                    this._log.error(
-                        `File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, error
-                    );
+                    this._log.error(
+                        `File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)?.name} uploading has failed!`, error
+                    );

Also applies to: 273-275

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts`
around lines 234 - 236, The failure log is printing the File object instead of
its name; update the calls to this._log.error in the upload-failure paths (the
one using this.dataField.stringId and
this.fileUploadEl.nativeElement.files.item(0) and the similar block around lines
273-275) to reference the file's name property (use
this.fileUploadEl.nativeElement.files.item(0).name) so the log matches the
success-path format and operators see the actual filename; keep the same
message/context and still pass response.error as the second argument to
this._log.error.

Comment on lines +258 to +261
const frontActions: Array<FrontAction> = this._eventService.parseFrontActionsFromOutcomeTree(response.outcome);
if (frontActions?.length > 0) {
this._frontActionService.runAll(frontActions);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the service still dereferences fn unconditionally.
rg -nP -C3 'public\s+run\s*\(' --type=ts -g '**/front-action.service.ts'
# And check parseFrontActionsFromOutcomeTree return contract.
ast-grep --pattern 'parseFrontActionsFromOutcomeTree($_) { $$$ }'

Repository: netgrif/components

Length of output: 891


🏁 Script executed:

# Get the full implementation of the run() method and surrounding context
cat -n projects/netgrif-components-core/src/lib/actions/services/front-action.service.ts | head -30

Repository: netgrif/components

Length of output: 1156


🏁 Script executed:

# Also check the runAll() method to understand the call pattern
rg -nP -A 10 'runAll\s*\(' --type=ts -g '**/front-action.service.ts'

Repository: netgrif/components

Length of output: 596


🏁 Script executed:

# Check the context around the runAll call in the file-field component
rg -nP -B5 -A5 'runAll\s*\(' --type=ts -g '**/abstract-file-default-field.component.ts'

Repository: netgrif/components

Length of output: 2295


Fix unhandled TypeError when unregistered action IDs are in upload outcome.

In FrontActionService.run() (projects/netgrif-components-core/src/lib/actions/services/front-action.service.ts:15-21), the if-guard logs an error when the action ID is missing but lacks a return statement, allowing execution to proceed to fn.call(...) with undefined. When parseFrontActionsFromOutcomeTree() returns an entry with an unknown ID, runAll() iterates through and invokes the faulty run(), throwing TypeError: Cannot read properties of undefined (reading 'call') inside the subscriber after the upload succeeds. Add a return statement in the if-block to prevent this unhandled exception.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts`
around lines 258 - 261, FrontActionService.run currently logs an error when an
action ID is not registered but does not return, causing fn to be undefined and
a TypeError when fn.call(...) is invoked; update FrontActionService.run so that
inside the if-block that checks for missing action (the block that logs the
error) you add an early return to exit the method and avoid calling fn.call,
ensuring runAll() and subscribers won’t throw when
parseFrontActionsFromOutcomeTree yields unknown IDs.

Comment on lines +178 to 183
this.state.completed = true;
this.state.uploading = false;
this.state.progress = 0;
this._log.debug(
`Files [${this.dataField.stringId}] were successfully uploaded`
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Premature success log: "successfully uploaded" is emitted before response.error is inspected.

_log.debug('Files ... were successfully uploaded') runs unconditionally on every non-progress response, including error outcomes handled just below. This pollutes logs with false success entries and can mislead incident triage. Move the debug log into the success branch (after line 197 where state.error = false).

🛠️ Proposed fix
                     } else {
                         this.state.completed = true;
                         this.state.uploading = false;
                         this.state.progress = 0;
-                        this._log.debug(
-                            `Files [${this.dataField.stringId}] were successfully uploaded`
-                        );
                         if (response.error) {
@@
                         } else {
                             const changedFieldsMap: ChangedFieldsMap = this._eventService.parseChangedFieldsFromOutcomeTree(response.outcome);
                             this.dataField.emitChangedFields(changedFieldsMap);
                             this.state.error = false;
+                            this._log.debug(
+                                `Files [${this.dataField.stringId}] were successfully uploaded`
+                            );
                             filesToUpload.forEach(fileToUpload => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.ts`
around lines 178 - 183, The debug message "Files [...] were successfully
uploaded" is logged unconditionally on non-progress responses, causing false
success logs; move the _log.debug(...) call into the success branch so it only
runs when response.error is falsy—specifically place it after the code that sets
this.state.error = false (the success handling block in the poll/response
handler inside abstract-file-list-default-field component), so that only
successful responses trigger the debug log and error responses do not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix improvement New feature or request Medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant