Skip to content

[PM-33981] feat: Add device management models and API layer#2870

Open
andrebispo5 wants to merge 5 commits into
mainfrom
pm-33981/device-table-core
Open

[PM-33981] feat: Add device management models and API layer#2870
andrebispo5 wants to merge 5 commits into
mainfrom
pm-33981/device-table-core

Conversation

@andrebispo5

Copy link
Copy Markdown
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-33981

📔 Objective

Adds the complete data and API layer for the device management feature — no UI changes in this PR.

  • Promotes DeviceType from an Int typealias to a proper enum DeviceType: Int in BitwardenKit, with a safe Decodable fallback to .unknownBrowser for unknown server values
  • Adds DeviceTypeCategory and DeviceType display extensions (category, platform, displayName, pendingRequestMatchKey) in BitwardenShared Core
  • Adds DeviceActivityStatus enum mapping server timestamps to activity buckets using calendar-day comparison
  • Adds DeviceListItem domain model, DeviceResponse/DevicesListResponse API models, CurrentDeviceRequest/DevicesListRequest, and DeviceAPIService with getCurrentDevice(appId:) and getDevices() endpoints
  • Promotes deviceAPIService to a proper ServiceContainer init parameter (consistent with all other services); updates withMocks() default to MockDeviceAPIService()
  • Adds unit tests for all new types and request/response models; adds JSON fixtures

📸 Screenshots

No UI changes in this PR.

- Promote DeviceType from Int typealias to a proper enum in BitwardenKit
  with a safe Decodable fallback to .unknownBrowser for unknown values;
  update DefaultHeadersRequestHandler and IdentityTokenRequestModel to
  use .rawValue where an Int is required
- Add DeviceTypeCategory and DeviceType display extensions (category,
  platform, displayName, pendingRequestMatchKey) in BitwardenShared Core
- Add DeviceActivityStatus enum mapping server dates to activity buckets
  using calendar-day comparison
- Add DeviceListItem domain model, DeviceResponse/DevicesListResponse API
  models, CurrentDeviceRequest/DevicesListRequest, and DeviceAPIService
  with getCurrentDevice(appId:) and getDevices() methods
- Add deviceAPIService as a proper init parameter to ServiceContainer
  (consistent with all other services); update withMocks() default to
  MockDeviceAPIService()
- Add tests for all new types and request/response models; add JSON
  fixtures CurrentDevice.json and DevicesList.json
Copilot AI review requested due to automatic review settings July 9, 2026 09:06
@andrebispo5
andrebispo5 requested review from a team and matt-livefront as code owners July 9, 2026 09:06
@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context app:authenticator Bitwarden Authenticator app context t:feature labels Jul 9, 2026
@andrebispo5 andrebispo5 added the ai-review Request a Claude code review label Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the device management data and API layer: the DeviceType promotion from Int typealias to a proper enum in BitwardenKit, the new DeviceTypeCategory/DeviceActivityStatus models, DeviceResponse/DevicesListResponse API models with requests, the DeviceAPIService endpoints, and the ServiceContainer wiring. The DeviceType migration is complete — all former Int usages (DefaultHeadersRequestHandler, IdentityTokenRequestModel, Constants.deviceType) now correctly use .rawValue, and no Authenticator code depended on the old typealias. Unknown server values decode safely via @DefaultValue to .unknownBrowser, and the change is well covered by unit tests and JSON fixtures.

Code Review Details

No blocking findings.

The two remaining open threads appear handled: the DeviceActivityStatus bucket concern was resolved by switching to Android-aligned day-count logic (commits 57ef65f6d/332105e2f), and the ServiceContainer stored-property question was answered by the author. No new inline comments were posted to avoid duplicating existing threads.

@andrebispo5 andrebispo5 added the innovation Feature work related to Innovation Sprint or BEEEP projects label Jul 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds the device-management data/API layer (no UI), including new device request/response models, domain mapping helpers, and a promoted DeviceAPIService dependency in ServiceContainer.

Changes:

  • Introduces device API models/requests (DeviceResponse, DevicesListResponse, CurrentDeviceRequest, DevicesListRequest) and extends DeviceAPIService with getCurrentDevice(appId:) and getDevices().
  • Promotes DeviceType in BitwardenKit from an Int typealias to a real enum with safe decoding fallback; updates dependent request/header building to use rawValue.
  • Adds device display/mapping utilities (DeviceTypeCategory, DeviceActivityStatus, DeviceListItem) plus fixtures and unit tests.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated no comments.

Show a summary per file
File Description
BitwardenShared/Core/Platform/Services/TestHelpers/ServiceContainer+Mocks.swift Adds injectable deviceAPIService to mocks container defaults.
BitwardenShared/Core/Platform/Services/ServiceContainer.swift Promotes deviceAPIService to an initializer dependency and stores it instead of computing from apiService.
BitwardenShared/Core/Auth/Services/API/Device/Requests/DevicesListRequestTests.swift Adds Swift Testing coverage for DevicesListRequest defaults.
BitwardenShared/Core/Auth/Services/API/Device/Requests/DevicesListRequest.swift Adds GET /devices request model.
BitwardenShared/Core/Auth/Services/API/Device/Requests/CurrentDeviceRequestTests.swift Adds Swift Testing coverage for CurrentDeviceRequest defaults/path interpolation.
BitwardenShared/Core/Auth/Services/API/Device/Requests/CurrentDeviceRequest.swift Adds GET /devices/identifier/{appId} request model.
BitwardenShared/Core/Auth/Services/API/Device/Fixtures/DevicesList.json Adds JSON fixture for devices list response decoding/tests.
BitwardenShared/Core/Auth/Services/API/Device/Fixtures/CurrentDevice.json Adds JSON fixture for current device response decoding/tests.
BitwardenShared/Core/Auth/Services/API/Device/Fixtures/APITestData+Device.swift Registers device fixtures for API tests.
BitwardenShared/Core/Auth/Services/API/Device/DeviceAPIServiceTests.swift Adds unit tests for new DeviceAPIService endpoints.
BitwardenShared/Core/Auth/Services/API/Device/DeviceAPIService.swift Extends protocol + APIService conformance with getCurrentDevice/getDevices.
BitwardenShared/Core/Auth/Models/Response/Fixtures/DeviceResponse+Fixtures.swift Adds test fixture builder for DeviceResponse.
BitwardenShared/Core/Auth/Models/Response/DevicesListResponseTests.swift Adds decoding test for DevicesListResponse.
BitwardenShared/Core/Auth/Models/Response/DevicesListResponse.swift Adds response wrapper model for list endpoint.
BitwardenShared/Core/Auth/Models/Response/DeviceResponseTests.swift Adds decoding test for DeviceResponse.
BitwardenShared/Core/Auth/Models/Response/DeviceResponse.swift Adds device API response model.
BitwardenShared/Core/Auth/Models/Request/IdentityTokenRequestModel.swift Updates device type form encoding to use DeviceType.rawValue.
BitwardenShared/Core/Auth/Models/Enum/DeviceType.swift Adds device type categorization + display utilities on BitwardenKit.DeviceType.
BitwardenShared/Core/Auth/Models/Enum/DeviceActivityStatusTests.swift Adds tests for activity bucketing and localization strings.
BitwardenShared/Core/Auth/Models/Enum/DeviceActivityStatus.swift Adds calendar-day-based bucketing for “last active” timestamps.
BitwardenShared/Core/Auth/Models/Domain/Fixtures/DeviceListItem+Fixtures.swift Adds test fixture builder for DeviceListItem.
BitwardenShared/Core/Auth/Models/Domain/DeviceListItem.swift Adds UI-friendly domain model + mapping init from DeviceResponse.
BitwardenResources/Localizations/en.lproj/Localizable.strings Adds localization keys for device category/status/display formatting.
BitwardenKit/Core/Platform/Utilities/Constants.swift Removes DeviceType typealias and updates constants to use new enum.
BitwardenKit/Core/Platform/Services/API/Handlers/DefaultHeadersRequestHandler.swift Updates device-type header value to use rawValue.
BitwardenKit/Core/Platform/Models/Enum/DeviceType.swift Introduces DeviceType: Int enum with safe decoding fallback.

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

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.02128% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.01%. Comparing base (85f3884) to head (332105e).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
...hared/Core/Auth/Models/Domain/DeviceListItem.swift 0.00% 13 Missing ⚠️
...d/Core/Auth/Models/Enum/DeviceActivityStatus.swift 96.36% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2870      +/-   ##
==========================================
- Coverage   81.27%   79.01%   -2.27%     
==========================================
  Files        1027     1157     +130     
  Lines       66109    73642    +7533     
==========================================
+ Hits        53733    58189    +4456     
- Misses      12376    15453    +3077     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…or backward compat

Tests that configure device API responses via MockHTTPClient (e.g.
LoginProcessorTests) continue to work unchanged. Callers that need
precise mock control pass MockDeviceAPIService explicitly.
Comment thread BitwardenKit/Core/Platform/Models/Enum/DeviceType.swift Outdated
Comment thread BitwardenShared/Core/Auth/Models/Domain/DeviceListItem.swift Outdated
Comment on lines +87 to +88
case 15 ... 30:
self = .thisMonth

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤔 Shouldn't "this month" only be the case if the month components are the same? If it's July 15, and the device was last active June 20 (25 days ago), this would return .thisMonth, but that's actually last month.

Comment thread BitwardenShared/Core/Platform/Services/ServiceContainer.swift
Comment thread BitwardenShared/Core/Auth/Models/Enum/DeviceTypeCategory.swift
Comment thread BitwardenKit/Core/Platform/Models/Enum/DeviceType.swift Outdated
- Use the DefaultValue property wrapper for DeviceResponse.type instead
  of a hand-rolled Decodable init, matching the CollectionType/
  CipherRepromptType pattern; add Sendable conformance to DefaultValue
  so it composes with Sendable response models
- Fix DeviceActivityStatus to classify "this month" by actual calendar
  month instead of a fixed 15-30 day range, which mislabeled dates from
  a previous calendar month (e.g. June 20 relative to a July 15 "now")
- Move DeviceListItem's DeviceResponse-based init into an extension so
  the compiler synthesizes the memberwise init instead of hand-writing it
- Rename BitwardenShared DeviceType.swift to DeviceTypeCategory.swift to
  match the DeviceTypeCategory enum it defines
- Add missing coverage: DeviceType/DeviceResponse unknown-value decode
  fallback, and DeviceTypeCategory/DeviceType category-platform-
  displayName-pendingRequestMatchKey mappings
Comment on lines +88 to +91
// Beyond the last two weeks, only classify as `.thisMonth` if the date actually
// falls within the current calendar month; a day count alone can't tell June 20
// apart from July 20 relative to a July 15 "now".
self = calendar.isDate(date, equalTo: now, toGranularity: .month) ? .thisMonth : .overThirtyDaysAgo

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤔 I still feel like this isn't quite right. Now if it's July 15, and the last activity was June 20, it would return .overThirtyDaysAgo even though it's less than 30 days. Is it worth adding an "X days ago" label to handle the case where it isn't this month but it also isn't over 30 days ago?

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 just noticed that this changed to match other clients, fixing it now.

Replace the calendar-month check for the .thisMonth bucket with pure
day-count thresholds (14-29 days), mirroring the Android app's
toLastActivityLabel logic so both platforms show identical "last
active" labels for the same date.
…ding

Rename ThisWeek/LastWeek/ThisMonth to PastSevenDays/PastFourteenDays/
PastThirtyDays and update their English values to "Past 7/14/30 days",
mirroring the Android app's past_seven_days/past_fourteen_days/
past_thirty_days string resources.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review app:authenticator Bitwarden Authenticator app context app:password-manager Bitwarden Password Manager app context innovation Feature work related to Innovation Sprint or BEEEP projects t:feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants