Skip to content

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

Merged
aj-rosado merged 4 commits into
mainfrom
PM-41291/identity-autofill-model-and-data-layer
Aug 26, 2026
Merged

[PM-41291] feat: Add model and data layer for Identity Autofill#7232
aj-rosado merged 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 current head of the identity autofill model/data layer: the new AutofillCipher.Identity, AutofillPartition.Identity, 17 AutofillView.Identity subclasses and matching AutofillHint entries, plus getIdentityAutofillCiphers() on both AutofillCipherProvider implementations and every exhaustive when the new sealed members force open. I traced each deferred branch and confirmed the PR is behavior-neutral as described: no code path produces an IDENTITY_* hint, AutofillParserImpl returns Unfillable for an Identity focused view, FilledDataBuilderImpl returns an empty partition list, and toAutofillSaveItem() returning null is correct because AutofillPartition.Identity.requiredSaveIds is empty so SaveInfoBuilderImpl never builds a SaveInfo — the AutofillProcessorImpl chain then falls through to saveCallback.onSuccess() with no change for Card/Login. The identity provider mirrors the card path for the security-relevant filters (vault-unlocked guard, isActive, CipherRepromptType.NONE), no identity PII is logged or persisted, and the two earlier review threads are resolved in the code: the display formatter is no longer reused for filling (identityAutofillAddress/identityAutofillName are separate, placeholder- and newline-free) and the AutofillHint when is fully enumerated rather than falling to else.

The one deferred branch that yields a value rather than an inert result is FilledDataExtensions.kt:43, mapping an Identity partition to AutofillSelectionData.Type.LOGIN; it is unreachable today and documented, and the enum gains an IDENTITY value in the phase that makes it reachable. Tests accompany each new branch and formatter, including the empty/partial address cases.

Code Review Details

No findings at or above the reporting threshold.

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 32.43243% with 100 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.66%. Comparing base (36bd55a) to head (f33ce8d).

Files with missing lines Patch % Lines
...twarden/data/autofill/parser/AutofillParserImpl.kt 0.00% 32 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 ⚠️
...arden/data/autofill/util/AutofillViewExtensions.kt 0.00% 12 Missing and 5 partials ⚠️
...ui/vault/feature/item/util/CipherViewExtensions.kt 62.50% 0 Missing and 3 partials ⚠️
...den/data/autofill/builder/FilledDataBuilderImpl.kt 0.00% 2 Missing ⚠️
...bitwarden/data/autofill/util/ViewNodeExtensions.kt 0.00% 2 Missing ⚠️
...n/data/autofill/processor/AutofillProcessorImpl.kt 87.50% 0 Missing and 1 partial ⚠️
...twarden/data/autofill/util/FilledDataExtensions.kt 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7232      +/-   ##
==========================================
+ Coverage   85.22%   85.66%   +0.43%     
==========================================
  Files        1033      922     -111     
  Lines       68135    66484    -1651     
  Branches     9940     9970      +30     
==========================================
- Hits        58070    56952    -1118     
+ Misses       6575     5998     -577     
- Partials     3490     3534      +44     
Flag Coverage Δ
app-data 17.62% <27.02%> (-0.30%) ⬇️
app-ui-auth-tools 18.80% <2.02%> (-0.49%) ⬇️
app-ui-platform 16.39% <0.00%> (+0.02%) ⬆️
app-ui-vault 27.23% <3.37%> (-0.70%) ⬇️
authenticator 6.06% <0.00%> (-0.03%) ⬇️
lib-core-network-bridge 4.07% <0.00%> (-0.03%) ⬇️
lib-data-ui 1.20% <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

@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
@aj-rosado
aj-rosado force-pushed the PM-41291/identity-autofill-model-and-data-layer branch from 4de9092 to f33ce8d Compare August 26, 2026 11:29
@aj-rosado
aj-rosado added this pull request to the merge queue Aug 26, 2026
Merged via the queue into main with commit da2ea37 Aug 26, 2026
23 checks passed
@aj-rosado
aj-rosado deleted the PM-41291/identity-autofill-model-and-data-layer branch August 26, 2026 12:01
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