feat: Improve import rollback - #5137
Conversation
There was a problem hiding this comment.
Pull request overview
Improves import rollback semantics to support showing failed-import errors in the UI by preserving import_control rows for genuine failures, while also optimizing “full rollback” by rolling back multiple imports in a single mutation call (per #4447).
Changes:
- Add a data-only rollback mutation (
rollbackImportData) for importer-side failure handling soimport_controlcan remain for UI visibility. - Extend importer unlock/stop-time mutation to persist
import_errorsand update rollback exception handling to keep/delete the import record based on failure/shutdown conditions. - Update UI full-management rollback to fetch all import IDs and rollback them in one call; update rollback GraphQL to accept an ID list.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| roles/ui/files/FWO.UI/Shared/ImportRollback.razor | Switch UI rollback to list-based mutation and add query to rollback all imports for a management in one call. |
| roles/tests-unit/files/FWO.Test/ImportRollbackQueriesTest.cs | Add unit tests asserting list-based rollback query shape and new management import-id query. |
| roles/lib/files/FWO.Api.Client/Queries/ImportQueries.cs | Register new getImportIdsByManagement GraphQL query text. |
| roles/importer/files/importer/test/test_import_rollback.py | Add tests for “keep import record on genuine failure”, data-only rollback usage, and import error persistence. |
| roles/importer/files/importer/model_controllers/fwconfig_import_rollback.py | Switch importer rollback to data-only GraphQL mutation and list-based variables. |
| roles/importer/files/importer/fwo_api_call.py | Extend unlock_import()/complete_import() to forward exception text into import_errors. |
| roles/importer/files/importer/common.py | Update rollback exception handler to keep or delete the import record depending on rollback/shutdown conditions. |
| roles/common/files/fwo-api-calls/import/updateImportStopTime.graphql | Add $importErrors variable and persist it to import_errors column. |
| roles/common/files/fwo-api-calls/import/rollbackImportData.graphql | Add new data-only rollback mutation that does not delete import_control. |
| roles/common/files/fwo-api-calls/import/rollbackImport.graphql | Update full rollback mutation to accept a list of import IDs (_in) and delete multiple import_control rows. |
| roles/common/files/fwo-api-calls/import/getImportIdsByManagement.graphql | Add query to fetch all import IDs for a management. |
Suppressed comments (1)
roles/ui/files/FWO.UI/Shared/ImportRollback.razor:160
- Typo in log message: "mangement" -> "management".
Log.WriteDebug("Delete Import", $"rolled back all imports of mangement with ID {ManagementId}");
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Suppressed comments (2)
roles/ui/files/FWO.UI/Shared/ImportRollback.razor:83
- The result of SendQueryAsync is assigned to a PascalCase local ("ReturnId") that is never used. This creates noise (unused variable warnings) and also shadows the ReturnId type name. Consider discarding the result explicitly.
var ReturnId = await apiConnection.SendQueryAsync<ReturnId>(FWO.Api.Client.Queries.ImportQueries.rollbackImport, variablesRollback);
roles/importer/files/importer/fwo_api_call.py:390
- exception_message can become None even when an exception exists (e.g., if an exception type defines a .message attribute that is None/empty). That would store import_errors as null and can hide the failure reason in the UI. Prefer a robust fallback to str(exception) whenever the extracted message is missing/empty.
exception_message: str | None = None
if exception is not None:
exception_message = getattr(exception, "message", None) if hasattr(exception, "message") else str(exception)
self.unlock_import(import_state, success=exception is None, import_errors=exception_message)
Y4nnikH
left a comment
There was a problem hiding this comment.
Keeping the import_control entries for failed import (which is obv needed), may have an impact on other functionality that checks on import_control, like e.g. the fw change trigger of flow and owner mapping job. Please make sure that all queries to import_control properly filter out unsuccessful imports.
Y4nnikH
left a comment
There was a problem hiding this comment.
Found one high-severity regression:
- F1 — rollback failures are reported as success. (ImportRollback.razor:81) catches either new mutation failure, but callers still show success; full rollback also deletes the latest configuration. A failure of the new second deleteImportControl request can leave import records behind while claiming rollback succeeded.
Recommendation: propagate the failure (or return success status), and only show success/delete latest config after both mutations complete. Add a failure-path test for each request.
|
Also please update the revision-history |
406fc81 to
a0b4adb
Compare
|



closes #4447