10000 feat: [#508] Add Restore method for Orm, to recover soft deleted data by hwbrzzl · Pull Request #775 · goravel/framework · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: [#508] Add Restore method for Orm, to recover soft deleted data #775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 20, 2024

Conversation

hwbrzzl
Copy link
Contributor
@hwbrzzl hwbrzzl commented Dec 18, 2024

📑 Description

Closes goravel/goravel#508

Implementing the Restore method and Restoring, Restored events.

facades.Orm().Query().WithTrashed().Restore(&user)
facades.Orm().Query().Model(&user).WithTrashed().Restore()

Summary by CodeRabbit

  • New Features

    • Introduced new methods for restoring soft-deleted models.
    • Added new observer interfaces for more granular event handling.
    • Enhanced event handling for the User model with new restoration events.
  • Bug Fixes

    • Corrected casing for EventUpdated constant.
  • Documentation

    • Updated error handling constants for improved clarity on ORM operations.
  • Tests

    • Expanded test coverage for event handling related to restoration and deletion of records.
  • Chores

    • Removed outdated methods from mock types to simplify testing.

✅ Checks

  • Added test cases for my code

@hwbrzzl hwbrzzl requested a review from a team as a code owner December 18, 2024 15:43
8000 Copy link
Contributor
coderabbitai bot commented Dec 18, 2024

Walkthrough

The pull request introduces a comprehensive enhancement to the ORM (Object-Relational Mapping) system, focusing on implementing a Restore method for recovering soft-deleted records. The changes span multiple files across the database and ORM packages, introducing new event interfaces, mock types, and methods to support the restoration of soft-deleted data. The implementation allows for restoring individual or multiple models using different method signatures.

Changes

File Change Summary
contracts/database/orm/events.go Reordered event types and corrected EventUpdated constant casing
contracts/database/orm/observer.go Replaced single Observer interface with granular event-specific interfaces
contracts/database/orm/orm.go Added Restore method to Query interface
database/gorm/query.go Implemented Restore method with event handling and column detection
database/gorm/test_models.go Added event handlers for restoration events
errors/list.go Replaced OrmUnexpectedQueryType with OrmDeletedAtColumnNotFound
support/database/database.go Updated GetIDByReflect to use primary key tag detection
Various mocks/database/orm/ files Added mock types for new event-specific observer interfaces

Sequence Diagram

sequenceDiagram
    participant Client
    participant ORM
    participant Model
    participant Database

    Client->>ORM: Restore(model)
    ORM->>ORM: Trigger Restoring Event
    ORM->>Database: Update DeletedAt column
    ORM->>Model: Trigger Restored Event
    ORM-->>Client: Return Result
Loading

Assessment against linked issues

Objective Addressed Explanation
Restore soft-deleted data
Support multiple restoration methods Supports restoration via WithTrashed() and direct model restoration
Trigger restoration events Added Restoring and Restored event interfaces

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @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.

Copy link
codecov bot commented Dec 18, 2024

Codecov Report

Attention: Patch coverage is 74.81481% with 34 lines in your changes missing coverage. Please review.

Project coverage is 69.91%. Comparing base (8e5426f) to head (2ed1065).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
database/gorm/query.go 73.27% 23 Missing and 8 partials ⚠️
support/database/database.go 57.14% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #775      +/-   ##
==========================================
- Coverage   69.95%   69.91%   -0.05%     
==========================================
  Files         213      213              
  Lines       18172    18246      +74     
==========================================
+ Hits        12713    12756      +43     
- Misses       4762     4785      +23     
- Partials      697      705       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@hwbrzzl hwbrzzl marked this pull request as draft December 20, 2024 03:56
Copy link
@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 2ed1065 Previous: 8e5426f Ratio
Benchmark_DecryptString 6150 ns/op 1592 B/op 17 allocs/op 2037 ns/op 1592 B/op 17 allocs/op 3.02
Benchmark_DecryptString - ns/op 6150 ns/op 2037 ns/op 3.02

This comment was automatically generated by workflow using github-action-benchmark.

Saving(Event) error
}

type ObserverWithUpdating interface {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Don't initiate these methods in the observer file. Add them according to requirement.

@hwbrzzl hwbrzzl marked this pull request as ready for review December 20, 2024 08:43
Copy link
Contributor
@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: 2

♻️ Duplicate comments (2)
mocks/database/orm/ObserverWithDeleting.go (1)

64-67: ⚠️ Potential issue

Fix incorrect implementation of RunAndReturn method

The same RunAndReturn implementation issue exists in this mock.

Apply the same fix as in ObserverWithUpdating.

mocks/database/orm/ObserverWithRetrieved.go (1)

64-67: ⚠️ Potential issue

Fix incorrect implementation of RunAndReturn method

The same RunAndReturn implementation issue exists in this mock.

Apply the same fix as in ObserverWithUpdating.

🧹 Nitpick comments (8)
database/gorm/query.go (2)

1603-1636: Consider adding fallback for custom DeletedAt field names.

The getDeletedAtColumn function loops over embedded fields for a gorm.DeletedAt type. This might miss custom field names or data structures for soft deletes. If that’s intended, it’s fine, but ensure the developer experience is documented or enforced consistently.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 1605-1606: database/gorm/query.go#L1605-L1606
Added lines #L1605 - L1606 were not covered by tests


[warning] 1615-1615: database/gorm/query.go#L1615
Added line #L1615 was not covered by tests


[warning] 1625-1625: database/gorm/query.go#L1625
Added line #L1625 was not covered by tests


[warning] 1635-1635: database/gorm/query.go#L1635
Added line #L1635 was not covered by tests


1694-1701: Observer event retrieval for restored/restoring is well-structured.

The extended getObserverEvent method now neatly includes EventRestored and EventRestoring. Confirm that all newly introduced events (e.g., “restoring”/“restored”) are tested with at least one observer to guarantee coverage.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 1694-1701: database/gorm/query.go#L1694-L1701
Added lines #L1694 - L1701 were not covered by tests

database/gorm/query_test.go (2)

1447-1467: Add concurrency or multi-model coverage for TestEvent_Restored.

The test only verifies a single record scenario. Consider testing multiple restores at once or partial restores, verifying the correct event firing for each model instance.


1469-1489: Expand test coverage for negative Restoring events.

TestEvent_Restoring thoroughly checks normal flow, but lacks negative scenarios (e.g., if restoration fails). This would strengthen confidence in error handling and event flow.

support/database/database.go (1)

30-37: Enhance coverage for empty primary key checks.

You handle empty strings and zero numeric IDs. Lines 33-34 are uncovered by tests. Add a test scenario with a struct containing an empty primary key to confirm that nil is returned as expected.

Would you like help drafting a test snippet for this case?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 33-34: support/database/database.go#L33-L34
Added lines #L33 - L34 were not covered by tests

contracts/database/orm/observer.go (1)

14-58: Well-structured interface segregation for lifecycle events

The breakdown of the monolithic Observer interface into smaller, focused interfaces follows the Interface Segregation Principle (ISP) effectively. This allows consumers to implement only the lifecycle events they need, reducing coupling.

The new ObserverWithRestoring and ObserverWithRestored interfaces align well with the PR's objective of implementing soft delete recovery.

Consider documenting the recommended usage patterns for these interfaces in the package documentation, especially focusing on:

  • When to use each interface
  • Common implementation patterns
  • Integration with the new Restore functionality
mocks/database/orm/ObserverWithRetrieved.go (1)

1-81: Consider implementing integration tests for observer chain

These mocks are part of the event system for the Restore functionality. To ensure robust testing:

  1. Implement integration tests that verify the correct order of events (Deleting → Retrieved → Updating) during restore operations
  2. Add test cases for error propagation through the observer chain
  3. Verify that all observers are properly cleaned up after tests

Would you like me to provide example test cases for these scenarios?

contracts/database/orm/orm.go (1)

129-130: Add documentation comments for the Restore method.

The new Restore method should include documentation comments explaining:

  • Its purpose and behavior
  • The relationship with WithTrashed()
  • Any prerequisites or side effects

Add documentation comments like this:

+// Restore restores one or more soft deleted models.
+// Must be used with WithTrashed() to include soft deleted records in the query scope.
+// Returns the number of restored records and any error that occurred.
 Restore(model ...any) (*Result, error)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8e5426f and 2ed1065.

📒 Files selected for processing (20)
  • contracts/database/orm/events.go (1 hunks)
  • contracts/database/orm/observer.go (1 hunks)
  • contracts/database/orm/orm.go (1 hunks)
  • database/console/stubs.go (0 hunks)
  • database/gorm/query.go (7 hunks)
  • database/gorm/query_test.go (4 hunks)
  • database/gorm/test_models.go (1 hunks)
  • errors/list.go (1 hunks)
  • mocks/database/orm/Observer.go (0 hunks)
  • mocks/database/orm/ObserverWithCreating.go (1 hunks)
  • mocks/database/orm/ObserverWithDeleting.go (1 hunks)
  • mocks/database/orm/ObserverWithForceDeleting.go (1 hunks)
  • mocks/database/orm/ObserverWithRestored.go (1 hunks)
  • mocks/database/orm/ObserverWithRestoring.go (1 hunks)
  • mocks/database/orm/ObserverWithRetrieved.go (1 hunks)
  • mocks/database/orm/ObserverWithSaved.go (1 hunks)
  • mocks/database/orm/ObserverWithSaving.go (1 hunks)
  • mocks/database/orm/ObserverWithUpdating.go (1 hunks)
  • mocks/database/orm/Query.go (1 hunks)
  • support/database/database.go (1 hunks)
💤 Files with no reviewable changes (2)
  • database/console/stubs.go
  • mocks/database/orm/Observer.go
✅ Files skipped from review due to trivial changes (3)
  • mocks/database/orm/ObserverWithCreating.go
  • mocks/database/orm/ObserverWithRestored.go
  • mocks/database/orm/ObserverWithRestoring.go
🧰 Additional context used
🪛 GitHub Check: codecov/patch
support/database/database.go

[warning] 33-34: support/database/database.go#L33-L34
Added lines #L33 - L34 were not covered by tests

database/gorm/query.go

[warning] 658-659: database/gorm/query.go#L658-L659
Added lines #L658 - L659 were not covered by tests


[warning] 674-675: database/gorm/query.go#L674-L675
Added lines #L674 - L675 were not covered by tests


[warning] 678-679: database/gorm/query.go#L678-L679
Added lines #L678 - L679 were not covered by tests


[warning] 683-684: database/gorm/query.go#L683-L684
Added lines #L683 - L684 were not covered by tests


[warning] 687-688: database/gorm/query.go#L687-L688
Added lines #L687 - L688 were not covered by tests


[warning] 1605-1606: database/gorm/query.go#L1605-L1606
Added lines #L1605 - L1606 were not covered by tests


[warning] 1615-1615: database/gorm/query.go#L1615
Added line #L1615 was not covered by tests


[warning] 1625-1625: database/gorm/query.go#L1625
Added line #L1625 was not covered by tests


[warning] 1635-1635: database/gorm/query.go#L1635
Added line #L1635 was not covered by tests


[warning] 1694-1701: database/gorm/query.go#L1694-L1701
Added lines #L1694 - L1701 were not covered by tests

🔇 Additional comments (16)
database/gorm/query.go (2)

1333-1347: Events hooking for delete/forceDelete operations are correct.

You have introduced separate methods (deleting, deleted, forceDeleting, forceDeleted), which is good for event hooking. However, confirm if the conventions remain consistent with all relevant model states.


1537-1537: Ensure consistent concurrency handling in update method.

This “update” method can be invoked concurrently. Consider verifying whether any concurrency or transaction usage might require a row-level lock or transaction pattern to avoid mid-update race conditions.

database/gorm/query_test.go (2)

3050-3094: Test multiple restoration flows in a single test method.

The TestRestore method verifies various “WithTrashed” usage patterns, which is thorough. However, lines 3069-3072 were specifically flagged for coverage. Add a scenario for attempting to restore an already active record or a model missing a deleted-at column to ensure robust coverage.


3772-3786: TestGetDeletedAtColumnName is well structured.

These lines confirm that embedded gorm.DeletedAt is recognized. Everything looks good with broad coverage for nested structs.

contracts/database/orm/events.go (1)

10-16: Enforce naming consistency.

You changed EventUpdated to “updated” for consistency. Everything else is neatly grouped. Ensure that any references or string matching outside this file are also updated (including logs or external references).

contracts/database/orm/observer.go (1)

29-37: Verify restore event ordering and error handling

The new restore-related interfaces look good, but we should ensure proper event ordering and error handling in the implementation.

Let's verify the usage of these interfaces in the codebase:

✅ Verification successful

Let me gather more information about the implementation to verify the event ordering and error handling.


Event ordering and error handling are properly implemented

The implementation in database/gorm/query.go shows correct event ordering and proper error handling:

  1. The Restore operation follows a clear sequence:
    • Calls restoring(model) before the actual restore operation
    • Performs the restore operation
    • Calls restored(model) after successful restore
  2. Error handling is robust:
    • Both restoring and restored events properly propagate errors (lines 677-679 and 686-688)
    • Each event handler returns early if an error occurs
    • The error chain is maintained throughout the operation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for implementations and usages of restore events
echo "Searching for restore event implementations..."
rg -l "ObserverWithRestor(ing|ed)" --type go

echo "Searching for restore event handling..."
rg "Restor(ing|ed)\(" --type go -A 5

Length of output: 4823


Script:

#!/bin/bash
# Search for the implementation of restore functionality in gorm query
echo "Searching for restore implementation in query.go..."
rg "func.*[Rr]estore" database/gorm/query.go -A 20

echo "\nSearching for restore event firing..."
ast-grep --pattern 'if err := $_; err != nil {
  return $$$
}'  database/gorm/query.go

Length of output: 7650

mocks/database/orm/ObserverWithSaved.go (1)

1-81: Auto-generated mock implementation looks correct

This mock file is auto-generated by mockery and follows the expected patterns for mock implementations.

mocks/database/orm/ObserverWithSaving.go (1)

1-81: Auto-generated mock implementation looks correct

This mock file is auto-generated by mockery and follows the expected patterns for mock implementations.

mocks/database/orm/ObserverWithRetrieved.go (1)

23-39: Verify Retrieved event handling for restored records

Since this mock is crucial for the Restore functionality, ensure that the Retrieved event is properly triggered when records are restored.

mocks/database/orm/ObserverWithForceDeleting.go (2)

23-39: LGTM! The ForceDeleting mock implementation is well-structured.

The implementation includes proper error handling for unspecified return values and supports both direct returns and function callbacks.


69-81: LGTM! The mock constructor follows best practices.

The NewObserverWithForceDeleting constructor properly:

  • Registers with the testing interface
  • Sets up automatic expectation verification via cleanup
  • Returns a properly initialized mock instance
errors/list.go (1)

105-105: LGTM! The new error constant is well-defined.

The OrmDeletedAtColumnNotFound error constant follows the established pattern and provides a clear error message for soft delete functionality.

contracts/database/orm/orm.go (1)

129-130: Verify the interaction between Restore and WithTrashed methods.

Let's verify that the implementation properly handles the relationship between these methods.

✅ Verification successful

Based on the results, I can now generate the final response:

Restore method implementation correctly handles WithTrashed interaction

The test coverage shows that:

  • Restore is properly used in conjunction with WithTrashed() in most cases
  • There's appropriate error handling for cases where deleted_at column is not found
  • Test cases cover various scenarios including:
    • Restoring with and without WithTrashed()
    • Restoring with different model configurations (single model, model slice)
    • Restoring with additional query conditions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if Restore is always used with WithTrashed in the codebase
# and verify test coverage for different scenarios

# Look for Restore usage patterns
echo "Checking Restore usage patterns:"
rg -A 2 '\.Restore\(' 

# Look for test cases covering Restore functionality
echo "Checking test coverage:"
rg -A 5 'TestRestore' 

# Check for potential error handling patterns
echo "Checking error handling patterns:"
rg -A 3 'OrmDeletedAtColumnNotFound'

Length of output: 2728

mocks/database/orm/Query.go (1)

2393-2458: LGTM: Restore method mock implementation is complete and consistent

The implementation includes:

  • Proper method signature with variadic parameters
  • Complete error handling
  • All necessary mock helper types and methods
  • Consistent documentation
database/gorm/test_models.go (2)

309-316: LGTM! Consider adding test coverage validation.

The EventRestored handler implementation follows the established patterns and conventions. The handler correctly modifies the "name" attribute for test scenarios.

Let's verify the test coverage for this handler:

✅ Verification successful

Let me check the test coverage by examining the actual test cases.


The EventRestored handler is properly tested

The test coverage is comprehensive in query_test.go. The test case TestEvent_Restored verifies:

  • The initial state with "event_restored_name"
  • The handler's modification to "event_restored_name1"
  • The persistence of changes
  • The proper restoration behavior
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check test coverage for the Restored event handler
# Look for test cases that verify the Restored event behavior

rg -l "event_restored_name" --type go

Length of output: 94


Script:

#!/bin/bash
# Look for test cases related to the Restored event
rg "event_restored_name" -B 2 -A 2 database/gorm/query_test.go

Length of output: 546


317-322: LGTM! Ensure consistent behavior between Restoring and Restored events.

The EventRestoring handler implementation is well-structured and follows the established patterns. Consider adding validation to ensure that when both handlers are triggered in sequence, they maintain data consistency.

Let's verify the test coverage and consistency between the handlers:

✅ Verification successful

Both Restoring and Restored events are properly tested with consistent behavior

The test coverage shows that both events are thoroughly tested in separate test cases (TestEvent_Restoring and TestEvent_Restored), and they follow the same pattern:

  • Both handlers modify the name attribute in a consistent way (appending "1" to the name)
  • Both have proper test cases verifying the attribute modifications
  • Both test cases verify the state before and after the event handling
  • The handlers are tested independently to ensure clean test isolation

While the events aren't tested in sequence in a single test case, this is actually a good practice as it allows for better isolation and clearer failure diagnosis. The current implementation and test coverage are sufficient to ensure reliable behavior.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check test coverage and consistency between Restoring and Restored events
# Look for test cases that verify both events are triggered in sequence

rg -A 5 "event_restoring_name.*event_restored_name" --type go

Length of output: 63


Script:

#!/bin/bash
# Let's search for test cases involving these events separately and then analyze their relationship

# Search for event_restoring_name tests
echo "=== Searching for event_restoring_name tests ==="
rg -B 2 -A 5 "event_restoring_name" --type go

# Search for event_restored_name tests
echo -e "\n=== Searching for event_restored_name tests ==="
rg -B 2 -A 5 "event_restored_name" --type go

# Search for any test functions involving restore events
echo -e "\n=== Searching for restore event test functions ==="
rg -B 2 -A 5 "Test.*Restor.*Event" --type go

Length of output: 4016

@hwbrzzl hwbrzzl merged commit 3e77574 into master Dec 20, 2024
11 of 12 checks passed
@hwbrzzl hwbrzzl deleted the bowen/#508 branch December 20, 2024 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

✨ [Feature] Add Restore method for Orm, to recover soft deleted data
2 participants
0