Skip to content

[PM-41291] feat: Add model and data layer for Identity Autofill - #7232

Open
aj-rosado wants to merge 4 commits into
mainfrom
PM-41291/identity-autofill-model-and-data-layer
Open

[PM-41291] feat: Add model and data layer for Identity Autofill#7232
aj-rosado wants to merge 4 commits into
mainfrom
PM-41291/identity-autofill-model-and-data-layer

Conversation

@aj-rosado

Copy link
Copy Markdown
Contributor

🎟️ Tracking

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

📔 Objective

Phase A/B of Identity Autofill (PM-38138): adds the model layer and data-layer plumbing that later phases build on.

  • New model types: AutofillView.Identity, AutofillPartition.Identity, AutofillCipher.Identity, and the corresponding AutofillHint entries.
  • AutofillCipherProvider.getIdentityAutofillCiphers() (+ implementation) to fetch identity ciphers, mirroring the existing getCardAutofillCiphers().
  • Every exhaustive when forced by the new sealed-class members is completed now, either with permanent trivial logic or an explicit inert stub (e.g. AutofillRequest.Unfillable) commented to say which later phase replaces it — no behavior change yet.
  • A second AutofillCipherProvider implementation (CipherViewExtensions.kt's toAutofillCipherProvider(), used by the manual-selection/accessibility completion flow) is updated in parallel so the two don't drift.

This PR is intentionally behavior-neutral — nothing yet classifies a field as Identity, so none of this is reachable in production. Detection and fill land in later stacked phases (heuristic detection, fill-assist mapping, and the identity partition build-out).

📸 Screenshots

N/A — model/data layer only, no UI changes.

@aj-rosado aj-rosado added the ai-review Request a Claude code review label Aug 3, 2026
@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context t:feature Change Type - Feature Development labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the Identity Autofill model/data layer: new AutofillView.Identity, AutofillPartition.Identity, AutofillCipher.Identity, and AutofillHint.IDENTITY_* members, plus AutofillCipherProvider.getIdentityAutofillCiphers() in both the repository-backed and single-cipher (CipherView.toAutofillCipherProvider()) implementations. Verified the PR is behavior-neutral: nothing produces an IDENTITY_* hint or an AutofillView.Identity, so the parser's Unfillable return, the FilledDataBuilderImpl empty list, the toAutofillSaveItem() null, and the ViewNodeExtensions Unused mapping are all unreachable today. The new identityAutofillName/identityAutofillAddress formatters are correctly separated from the display-oriented identityAddress/identityName in the vault-item UI (no - placeholders or newlines), the nullable toAutofillSaveItem() change is handled correctly at the single call site in AutofillProcessorImpl, and the identity cipher filtering mirrors the existing card path (non-deleted, non-archived, no reprompt).

Code Review Details

No findings at or above the reporting threshold.

Notes considered and intentionally not raised as findings:

  • The AutofillPartition.Identity -> AutofillSelectionData.Type.LOGIN placeholder in FilledDataExtensions.kt:43 is unreachable today and is explicitly documented as deferred.
  • The previously raised formatting comment on AutofillParserImpl.kt appears addressed by the latest formatter commit.

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 20.86957% with 182 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.31%. Comparing base (7affa3d) to head (4de9092).

Files with missing lines Patch % Lines
...8bit/bitwarden/data/autofill/model/AutofillView.kt 0.00% 35 Missing ⚠️
...twarden/data/autofill/parser/AutofillParserImpl.kt 0.00% 32 Missing ⚠️
...it/bitwarden/data/autofill/model/AutofillCipher.kt 0.00% 25 Missing ⚠️
...ta/autofill/provider/AutofillCipherProviderImpl.kt 37.14% 0 Missing and 22 partials ⚠️
...twarden/data/autofill/util/CipherViewExtensions.kt 20.00% 0 Missing and 20 partials ⚠️
...8bit/bitwarden/data/autofill/model/AutofillHint.kt 0.00% 17 Missing ⚠️
...arden/data/autofill/util/AutofillViewExtensions.kt 0.00% 12 Missing and 5 partials ⚠️
...bitwarden/data/autofill/model/AutofillPartition.kt 0.00% 5 Missing ⚠️
...ui/vault/feature/item/util/CipherViewExtensions.kt 62.50% 0 Missing and 3 partials ⚠️
...den/data/autofill/builder/FilledDataBuilderImpl.kt 0.00% 2 Missing ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7232      +/-   ##
==========================================
- Coverage   86.19%   84.31%   -1.89%     
==========================================
  Files         936     1132     +196     
  Lines       66989    69160    +2171     
  Branches     9860    10047     +187     
==========================================
+ Hits        57742    58311     +569     
- Misses       5738     7267    +1529     
- Partials     3509     3582      +73     
Flag Coverage Δ
app-data 17.55% <27.02%> (-0.08%) ⬇️
app-ui-auth-tools 18.93% <2.02%> (-0.06%) ⬇️
app-ui-platform 16.86% <0.00%> (+0.11%) ⬆️
app-ui-vault 27.71% <2.17%> (-0.21%) ⬇️
authenticator 6.07% <0.00%> (-0.02%) ⬇️
lib-core-network-bridge 4.09% <0.00%> (-0.01%) ⬇️
lib-data-ui 1.19% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aj-rosado
aj-rosado marked this pull request as ready for review August 3, 2026 13:28
@aj-rosado
aj-rosado requested review from a team and david-livefront as code owners August 3, 2026 13:28
@aj-rosado aj-rosado added t:feature Change Type - Feature Development and removed t:feature Change Type - Feature Development labels Aug 3, 2026
Comment on lines +91 to +102
internal val IdentityView.identityAddress: String?
get() = listOfNotNull(
address1,
address2,
address3,
listOf(city ?: "-", state ?: "-", postalCode ?: "-")
.nullIfAllEqual("-")
?.joinToString(", "),
country,
)
.joinToString("\n")
.orNullIfBlank()

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.

⚠️ IMPORTANT: identityAddress is a display formatter; reusing it as AutofillCipher.Identity.fullAddress will fill - placeholders and newlines into form fields.

Details and fix

identityAddress was written for the vault item detail screen, where - placeholders and \n separators render acceptably. AutofillCipher.Identity.fullAddress is documented as "address parts joined for filling a combined full-address field", so the later fill phase will push this exact string into a PostalAddressFull input.

The new test in CipherViewExtensionsTest.kt locks in the problematic output:

assertEquals("address3\n-, state, -", identityView.identityAddress)

For a cipher with only state populated, the user's address field would be filled with a literal -, state, -. Newlines in a single-line EditText are also dropped or rendered as spaces depending on the app.

Suggested fix: keep identityAddress for display and add a separate autofill-oriented formatter that omits missing parts entirely (no - placeholders) and joins with ", " — e.g. listOfNotNull(address1, address2, address3, city, state, postalCode, country).joinToString(", ").orNullIfBlank().

Same root cause applies to identityName: it prefixes the title, so a PersonNameFull field would be filled with Mr John Doe even though PersonNamePrefix exists as its own view for the title.

Since fill is a no-op in this phase, this is not user-visible yet — but the shape is being locked in here and asserted by tests, so it's cheaper to correct now than in the fill phase.

@aj-rosado aj-rosado Aug 3, 2026

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.

@claude I agree with you. Reverted this method to the CipherViewExtensions it originally belonged and added a new one for the autofill

Comment on lines +256 to +262
// Identity hint detection/dispatch is wired up in a later phase; treating these as Unused for
// now is a no-op since nothing yet produces an IDENTITY_* hint.
else -> {
AutofillView.Unused(
data = autofillViewData,
)
}

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.

♻️ DEBT: The else -> branch removes when exhaustiveness over AutofillHint, so a missed hint in the next phase compiles silently.

Details and fix

Everywhere else in this PR the new sealed-class/enum members are enumerated explicitly, which is what makes the "later phase will have to touch this" guarantee work. Here the else does the opposite: after the 17 IDENTITY_* entries are added to AutofillHint, the compiler can no longer tell the next author which ones still need a dispatch case, and any hint they forget quietly becomes AutofillView.Unused — a silent no-fill rather than a build failure.

Suggested fix: list the identity hints in a single grouped branch instead of else:

AutofillHint.IDENTITY_PERSON_NAME_FULL,
AutofillHint.IDENTITY_PERSON_NAME_PREFIX,
// ... remaining IDENTITY_* entries
    -> {
    // Identity hint dispatch is wired up in a later phase.
    AutofillView.Unused(data = autofillViewData)
}

This keeps the same inert behavior while preserving the compile-time check.

@aj-rosado aj-rosado Aug 3, 2026

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.

@claude added all cases

@aj-rosado
aj-rosado force-pushed the PM-41291/identity-autofill-model-and-data-layer branch 3 times, most recently from 982905e to 6f0c34c Compare August 4, 2026 16:53
saveCallback.onSuccess(intentSender)
} else {
saveCallback.onSuccess()
}

@david-livefront david-livefront Aug 5, 2026

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.

Can we simplify this:

autofillRequest
    .toAutofillSaveItem()
    ?.let { autofillSaveItem ->
        createAutofillSavedItemIntentSender(
            autofillAppInfo = autofillAppInfo,
            autofillSaveItem = autofillSaveItem,
        )
    }
    ?.let { saveCallback.onSuccess(it) }
    ?: saveCallback.onSuccess()

}
?.let { nonNullCipherListView ->
nonNullCipherListView.id?.let { cipherId ->
decryptCipherOrNull(cipherId = cipherId)?.let { cipherView ->

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.

We shouldn't do this in this PR but we might want to consider optimizing this flow in the future with new DB functions.

We can make a make specific DB queries to fetch the relevant ciphers only, filtering by type, active, and reprompt. That should make for a fairly meaningful performance boost.

What you have here conforms to the existing pattern and seems perfectly fine for now though.

is AutofillPartition.Identity -> {
// Capturing identity data from a filled form is out of scope. This is never actually
// reached because AutofillPartition.Identity.canPerformSaveRequest is always false, so
// SaveInfo (and therefore a save callback) is never built for it.

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.

👍

val licenseNumber: String,
) : AutofillCipher() {
override val iconRes: Int
@DrawableRes get() = BitwardenDrawable.ic_id_card

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.

Is this the correct icon?

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.

yes 😅 The one for cards is ic_payment_card this is the used on each Identity scenario

postalCode = identityView?.postalCode.orEmpty(),
country = identityView?.country.orEmpty(),
company = identityView?.company.orEmpty(),
email = identityView?.email.orEmpty(),

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 see we have the orEmpty usages in the other spots too, does this cause autofill to clear the value if the user has already typed into the field?

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.

No, later on the flow we are ignoring empty fields

@aj-rosado
aj-rosado force-pushed the PM-41291/identity-autofill-model-and-data-layer branch 2 times, most recently from 0cc466b to 532e403 Compare August 12, 2026 14:47
is AutofillView.Identity.AddressCountry -> {
this.copy(data = this.data.copy(website = site))
}
is AutofillView.Identity.AddressLocality -> {

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.

Wanna run the formatter on this file, you should have some newlines in here

@aj-rosado
aj-rosado force-pushed the PM-41291/identity-autofill-model-and-data-layer branch from 47cfacc to 4de9092 Compare August 19, 2026 13:43
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:password-manager Bitwarden Password Manager app context t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants