Skip to content

feat: add restore and restore_by for soft-deletable models#7

Merged
Pentusha merged 1 commit into
mainfrom
feat/restore-soft-deletable
May 12, 2026
Merged

feat: add restore and restore_by for soft-deletable models#7
Pentusha merged 1 commit into
mainfrom
feat/restore-soft-deletable

Conversation

@Pentusha
Copy link
Copy Markdown
Member

Implements restore and restore_by methods for soft-deletable models across v1 and v2 architectures.

Changes

  • v2 Repository: SoftDeleteMixin.restore() / .restore_by()
    • Bypasses default soft-delete filters so deleted records can be targeted
  • v2 Service: SoftDeleteServiceMixin.restore() / .restore_by()
    • Supports actor_id for updated_by tracking
  • v1 Repository: SoftDeletableRepo.restore() / .restore_by()
  • v1 Service: SoftDeletableService.restore() / .restore_one_by() / .restore_all_raw_by()
  • Docs: Updated docs/v2/repositories.md and docs/v2/services.md with usage examples
  • Tests: Added integration tests for all new methods

Test results

All 346 tests pass, linters clean (ruff, mypy, pyright, stubtest).

Implements restore/restore_by across v1 and v2 architectures:

- v2 Repository: SoftDeleteMixin.restore() / .restore_by()
  Bypasses default soft-delete filters so deleted records can be restored.
- v2 Service: SoftDeleteServiceMixin.restore() / .restore_by()
  Supports actor_id for updated_by tracking.
- v1 Repository: SoftDeletableRepo.restore() / .restore_by()
- v1 Service: SoftDeletableService.restore() / .restore_one_by() / .restore_all_raw_by()
- Updated docs/v2 with usage examples
- Added integration tests for all new methods
Copilot AI review requested due to automatic review settings May 12, 2026 15:18
@Pentusha Pentusha merged commit 158bf53 into main May 12, 2026
13 checks passed
@Pentusha Pentusha deleted the feat/restore-soft-deletable branch May 12, 2026 15:20
Copy link
Copy Markdown

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

Adds “restore” capabilities to the existing soft-delete feature set, spanning both the v1 (Filter-based) and v2 (mixins/protocols + service mixins) architectures, plus docs and integration tests to validate the new behavior.

Changes:

  • Add restore / restore_by to v2 repositories (SoftDeleteMixin) and services (SoftDeleteServiceMixin), including actor_id support for updated_by.
  • Add restore / restore_by to v1 SoftDeletableRepo and corresponding service helpers.
  • Update v2 docs with restore usage examples and add integration tests covering restore flows.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/v2/test_integration/test_service_crud.py Adds service-level integration tests for restore flows (pk, by filters, raw, actor_id).
tests/v2/test_integration/test_repository_crud.py Adds repository-level integration tests for restore flows (pk, by filters, extra payload).
tests/v1/test_integration/test_base_repo.py Adds integration tests for v1 repo restore behavior.
src/notora/v2/services/mixins/delete.pyi Extends v2 service mixin typing surface with restore* overloads.
src/notora/v2/services/mixins/delete.py Implements restore* methods in SoftDeleteServiceMixin.
src/notora/v2/repositories/mixins/write.py Implements v2 repo restore / restore_by statement builders.
src/notora/v2/repositories/base.py Extends SoftDeleteRepositoryProtocol with restore* methods.
src/notora/v1/services/base.py Adds v1 SoftDeletableService.restore* helpers.
src/notora/v1/persistence/repos/base.py Implements v1 SoftDeletableRepo.restore* update statements.
docs/v2/services.md Documents service-level restore usage.
docs/v2/repositories.md Documents repository-level restore usage and intent.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -381,7 +381,116 @@ async def test_service_soft_delete_by_returns_list(

assert len(deleted) == len(payloads)
assert all(isinstance(d, V2UserResponseSchema) for d in deleted)
Comment on lines +188 to +192
payload.update(additional_payload)
stmt = update(self.model).values(**payload)
stmt = self.apply_filters(stmt, filters, apply_default_filters=False)
stmt = stmt.returning(self.model)
return self.apply_options(stmt, options)
Comment on lines +340 to +345
def restore_by(
self,
filters: Iterable[Filters] = (),
) -> TypedReturnsRows[tuple[ModelType]]:
query = update(self.model).values({'deleted_at': None})
for predicate in self._get_query_predicates(filters):
Comment thread docs/v2/repositories.md
Comment on lines +168 to +177
`restore` and `restore_by` clear `deleted_at` back to `NULL`. They bypass the
soft-delete default filter so they can target already-deleted records:

```python
# Restore by primary key
query = repo.restore(user_id)

# Restore by filters
query = repo.restore_by(
filters=[lambda m: m.email == "a@b.com"],
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.

2 participants