Skip to content

fix(models): handle missing primary field when deserializing Role (signed re-push of #44) - #45

Merged
shiftcontrol-dan merged 1 commit into
mainfrom
dan/signed-repush-role-primary
Aug 15, 2026
Merged

fix(models): handle missing primary field when deserializing Role (signed re-push of #44)#45
shiftcontrol-dan merged 1 commit into
mainfrom
dan/signed-repush-role-primary

Conversation

@shiftcontrol-dan

Copy link
Copy Markdown
Contributor

Summary

Signed re-push of @travipross' commit from #44 (that commit carried an S/MIME signature from an internal CA that GitHub reports as no_user, so it could not satisfy the signed-commits rule on main), plus an end-to-end regression test, a version bump, and changelog notes.

Role::primary gains #[serde(default)]. Applying deserialize_with suppresses serde's implicit handling of omitted Option<T> fields, so without an accompanying default a payload that simply omits primary was rejected with a missing field error rather than deserializing to None. This regressed 0.3 behaviour during the 0.4.x lenient-bool work.

#[serde(
    default, // required as `deserialize_with` does not set default when field is missing
    skip_serializing_if = "Option::is_none",
    deserialize_with = "deserialize_optional_lenient_bool"
)]
pub primary: Option<bool>,

Tests

Carried over from #44:

  • role_deserialization_with_omitted_primary_key — minimal reproduction isolating the bug to Role.
  • Six unit tests on deserialize_optional_lenient_bool in src/utils/serde.rs, including omitted_key_fails_without_serde_default, which pins the reason default is required at the call site.

Added here:

  • deserialize_github_enterprise_user_list, driven by a new src/test_data/github_enterprise_user_list_test.json fixture. This is the sanitized real GitHub Enterprise SCIM /Users response @travipross supplied in this comment. The first resource carries a role with primary absent ({"value": "enterprise_owner"}), the second an empty roles array. It exercises the failure at the ListResponse level, which is where it actually bit.

Both regression tests were confirmed to fail against the unfixed code, with the reported error:

role_deserialization_with_omitted_primary_key ... FAILED
  missing field `primary`, line: 1, column: 26
deserialize_github_enterprise_user_list ... FAILED
  missing field `primary`, line: 84, column: 4

Scope note

Devin flagged that the other multi-valued structs (Email, PhoneNumber, Im, Photo, Entitlement, X509Certificate) keep a plain Option<bool> and would still reject a stringified "true". Left as-is deliberately, for two reasons:

  • The missing-field bug only occurs where deserialize_with is present, since that is what suppresses serde's Option default. Role::primary is the only production use of deserialize_with in the crate, so there are no other latent instances. Plain Option<bool> fields already default to None when the key is absent.
  • The leniency itself is targeted, not general. It arrived in 8d5cc19 alongside src/test_data/entra_user_creation_test.json (from Microsoft's SCIM validator), where Entra sends a real boolean for primary on emails and phoneNumbers and a string only on roles. Widening it elsewhere has no driving case today.

Release

Tagging v0.4.2 after merge triggers publish + release.

Verification

cargo fmt --check, cargo clippy --all-targets -- -D warnings, and cargo test (137 unit + 37 doctests) all pass locally.

Original authorship is credited via Co-authored-by. #44 can be closed once this merges.

… (signed re-push of #44)

Fixes a regression introduced in the 0.4.x releases.

Applying `#[serde(deserialize_with = "deserialize_optional_lenient_bool")]`
to `Role::primary` suppressed serde's implicit handling of omitted
`Option<T>` fields, which would otherwise deserialize to `None`. Because
the field carried no `#[serde(default)]`, a payload omitting `primary`
was rejected with a `missing field` error.

Also bumps the crate to 0.4.2 and records the fix in the changelog.

Co-authored-by: Travis Prosser <travis.prosser@lastwall.com>
@shiftcontrol-dan
shiftcontrol-dan merged commit 07e8e70 into main Aug 15, 2026
4 checks passed
@shiftcontrol-dan
shiftcontrol-dan deleted the dan/signed-repush-role-primary branch August 15, 2026 03:27
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.

1 participant