mikebridge commented on PR #39977:
URL: https://github.com/apache/superset/pull/39977#issuecomment-4454315239

   @richardfogaca thanks for the careful pass. All four addressed in 
`5115202a3f` (infrastructure) plus mechanical entity-branch updates. Per-item:
   
   **Item 1 — `find_existing_for_import` side-effect split — fixed.**
   
   You're right that the destructive cleanup happening as a side effect of 
"find" is the wrong shape — today's flow is safe only because `can_write` is 
checked once at the import command's top level, but a future overwrite-path 
permission check would have a quiet bypass via the soft-delete path.
   
   `find_existing_for_import` is now a pure lookup that returns the row as-is 
(live OR soft-deleted) or `None`. A new 
`clear_soft_deleted_for_import(existing)` does the explicit hard-delete + ORM 
flush. Each entity's v1 importer (sc-106189/90/91) now calls them in the order 
you suggested: lookup → permission check on the existing row → only then 
`clear_soft_deleted_for_import` if the match was soft-deleted. The destructive 
op is at the call site, audit-able, and gated by the same ownership check as 
the live-overwrite path.
   
   **Item 2 — `commands/restore.py:65-69` `skip_base_filter=True` — fixed.**
   
   Agreed it had no business being there. `BaseRestoreCommand.validate` now 
passes only `skip_visibility_filter=True`. The entity's RBAC `base_filter` 
stays in effect, matching how `find_by_ids` is called on the existing delete 
paths. The `DashboardFilter`/`ChartFilter`/`DatasetFilter` are pure RBAC — they 
don't filter on `deleted_at`, so leaving them on is the correct scope. 
Ownership is still verified by `raise_for_ownership` immediately after, so the 
rejection codes line up with delete's behavior.
   
   Test renamed and tightened: 
`test_validate_calls_dao_with_visibility_bypass_only` asserts the DAO call no 
longer includes `skip_base_filter`.
   
   **Item 3 — `run()` transaction wrapping contract — fixed (refactored, not 
just documented).**
   
   You're right that "subclass overrides `run()` ONLY to add `@transaction`" 
was fragile, especially across multiple entity rollouts. The base 
`BaseRestoreCommand` now owns the transactional wrapper directly:
   
   ```python
   class BaseRestoreCommand(BaseCommand, Generic[T]):
       dao: ClassVar[Any]
       not_found_exc: ClassVar[type[Exception]]
       forbidden_exc: ClassVar[type[Exception]]
       restore_failed_exc: ClassVar[type[Exception]]  # new
   
       def run(self) -> None:
           @transaction(on_error=partial(on_error, 
reraise=self.restore_failed_exc))
           def _perform() -> None:
               model = self.validate()
               model.restore()
           _perform()
   ```
   
   The wrapper is built at call time so `on_error` can reference the 
per-subclass `restore_failed_exc` ClassVar. Concrete entity restore commands 
collapse to four ClassVar assignments with no methods — strictly declarative. 
Less to remember, no contract to silently skip.
   
   New test `test_run_translates_sqlalchemy_errors_via_restore_failed_exc` pins 
the wrapping: a `SQLAlchemyError` raised inside the unit of work is translated 
to the subclass's `restore_failed_exc`, so a refactor that drops the wrapper or 
points it at the wrong type fails CI.
   
   **Item 4 — `SoftDeleteMixin` docstring nit — fixed.**
   
   `BaseDAO.delete()` now routes through the mixin to `soft_delete()` for 
`SoftDeleteMixin` models; `BaseDAO.hard_delete()` is the permanent-deletion 
path. Docstring updated to point at `hard_delete()` for the permanent case.
   
   **Re: the `expunge_all()` praise** — thanks. `Query.get()` short-circuiting 
through the identity map can make tests look correct while the listener never 
fires; flushing the map made the empirical proof airtight. Glad it reads as 
such.
   
   ---
   
   Entity branches (sc-106189/90/91) all rebased onto the new infra tip and 
pushed. CI should re-run on all four PRs.
   
   Let me know if anything else surfaces.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to