Skip to content

Populate last access data in admin user details#435

Open
iammajid wants to merge 2 commits intodevelopfrom
feature/admin-last-access
Open

Populate last access data in admin user details#435
iammajid wants to merge 2 commits intodevelopfrom
feature/admin-last-access

Conversation

@iammajid
Copy link
Copy Markdown
Contributor

@iammajid iammajid commented Apr 1, 2026

Summary

  • Fetch devices with last access data (timestamp + IP) via a single LEFT JOIN query instead of separate queries
  • Add findByOwnerWithLastAccess() to Device.Repository and LegacyDevice.Repository
  • Remove eager device fetching from User.findByIdWithEagerDetails() since devices are now loaded via the joined queries
  • No new entity classes reuses existing VaultKeyRetrievedEvent and DeviceDto.fromEntity() overloads

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 619a503c-a435-475e-8ad3-96584b5454d1

📥 Commits

Reviewing files that changed from the base of the PR and between b1a09f6 and d193a5f.

📒 Files selected for processing (3)
  • backend/src/main/java/org/cryptomator/hub/api/UsersResource.java
  • backend/src/main/java/org/cryptomator/hub/entities/Device.java
  • backend/src/main/java/org/cryptomator/hub/entities/LegacyDevice.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/main/java/org/cryptomator/hub/api/UsersResource.java
  • backend/src/main/java/org/cryptomator/hub/entities/Device.java

Walkthrough

The pull request optimizes device retrieval in the UsersResource endpoint by introducing repository-level joined queries. Two new repository methods—findByOwnerWithLastAccess—are added to the Device and LegacyDevice entities. Each method executes a JPQL query that left-joins devices to their most recent VaultKeyRetrievedEvent and returns records pairing the device with its optional last access event. The UsersResource endpoint is updated to use these new methods instead of separately fetching devices and last-access information.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective of the PR: populating last access data when retrieving user details in the admin interface.
Description check ✅ Passed The description is directly related to the changeset, detailing the optimization of device fetching with last access data via joined queries and new repository methods.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/admin-last-access

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.

Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
backend/src/main/java/org/cryptomator/hub/entities/Device.java (1)

167-176: Consider using a typed projection for type safety.

The Object[] return type requires unchecked casts at the call site. A typed record could make the API clearer and catch misuse at compile time:

💡 Optional: Use a typed record for the result
public record DeviceWithLastAccess(Device device, VaultKeyRetrievedEvent lastAccessEvent) {}

public List<DeviceWithLastAccess> findByOwnerWithLastAccess(String userId) {
    return getEntityManager().createQuery("""
                    SELECT d, e FROM Device d
                    LEFT JOIN VaultKeyRetrievedEvent e ON e.deviceId = d.id
                        AND e.id = (SELECT MAX(e2.id) FROM VaultKeyRetrievedEvent e2 WHERE e2.deviceId = d.id)
                    WHERE d.owner.id = :userId
                    """, Object[].class)
            .setParameter("userId", userId)
            .getResultStream()
            .map(row -> new DeviceWithLastAccess((Device) row[0], (VaultKeyRetrievedEvent) row[1]))
            .toList();
}

This is optional since the current implementation is functionally correct and the casting is isolated to the repository layer.

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

In `@backend/src/main/java/org/cryptomator/hub/entities/Device.java` around lines
167 - 176, Replace the raw Object[] projection returned by
Device.findByOwnerWithLastAccess with a typed projection: add a record
DeviceWithLastAccess(Device device, VaultKeyRetrievedEvent lastAccessEvent) and
change findByOwnerWithLastAccess to run the same JPQL query but request
Object[].class, stream the results, map each Object[] row to new
DeviceWithLastAccess((Device)row[0], (VaultKeyRetrievedEvent)row[1]) and return
List<DeviceWithLastAccess>; keep the same parameter binding and query text in
getEntityManager().createQuery(...) and use getResultStream().map(...).toList()
for the conversion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/src/main/java/org/cryptomator/hub/entities/Device.java`:
- Around line 167-176: Replace the raw Object[] projection returned by
Device.findByOwnerWithLastAccess with a typed projection: add a record
DeviceWithLastAccess(Device device, VaultKeyRetrievedEvent lastAccessEvent) and
change findByOwnerWithLastAccess to run the same JPQL query but request
Object[].class, stream the results, map each Object[] row to new
DeviceWithLastAccess((Device)row[0], (VaultKeyRetrievedEvent)row[1]) and return
List<DeviceWithLastAccess>; keep the same parameter binding and query text in
getEntityManager().createQuery(...) and use getResultStream().map(...).toList()
for the conversion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b3d6d35f-0f40-4063-9ab5-4bd52d333bf7

📥 Commits

Reviewing files that changed from the base of the PR and between f5408b3 and b1a09f6.

📒 Files selected for processing (4)
  • backend/src/main/java/org/cryptomator/hub/api/UsersResource.java
  • backend/src/main/java/org/cryptomator/hub/entities/Device.java
  • backend/src/main/java/org/cryptomator/hub/entities/LegacyDevice.java
  • backend/src/main/java/org/cryptomator/hub/entities/User.java

});
}

public List<Object[]> findByOwnerWithLastAccess(String userId) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please replace Object[] by a dedicated type (record)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please explain this change - seems unrelated and changes behaviour in different places

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I initially removed the eager fetch to avoid loading devices twice, once in findByIdWithEagerDetails and again via the new findByOwnerWithLastAccess queries. But you're right, this method is shared and the change would affect other callers. Reverted.

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