refactor(core): typed credential registry replacing string-matched VC kinds (R19)#102
Merged
Merged
Conversation
… kinds (R19) Replace the hard-coded "MembershipCredential"/"EndorsementCredential" string-matching in `handle_credential_issue` and the two `Option<serde_json::Value>` fields on `CommunityRecord` with a single typed registry: a `CredentialKind` enum (kept next to `MessageType` in core) keying a `BTreeMap<CredentialKind, serde_json::Value> credentials`. Adding a credential kind is now one variant + its match arms — dispatch, storage and the My Credentials UI all iterate `CredentialKind::ALL` / match on `vc_type`, so they pick it up without edits. Config round-trip is preserved without a migration: a deserialize-only `CommunityRecordShadow` (`#[serde(from = ...)]`) folds pre-R19 flat `membership_credential`/`role_credential` fields into the map, new-format keys winning. The shadow keys the map by `String` deliberately so an unrecognised kind from a newer build is dropped with a warning rather than making the whole config unloadable. Behavior is equivalent on the substantive path (storage + membership activation). The router needs no change: all credential kinds share one `credential-exchange/issue` message type and DIDComm route, so a new kind cannot drift the route list. Tests: registry self-consistency; dispatch handles every registered kind generically (acceptance criterion); legacy flat-field load; new-format round-trip + unknown-key tolerance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
handle_credential_issuestring-matched"MembershipCredential"/"EndorsementCredential"and stored each into one of two hard-codedOption<serde_json::Value>fields onCommunityRecord. Adding a credentialtype meant touching dispatch, storage, and the UI — five places that could
drift out of sync (R19).
Fix
A single typed registry:
CredentialKindenum inopenvtc-core/src/lib.rs, kept next toMessageType. It owns thevc_type(W3C VCtypestring), the persistedconfig_key, andactivates_membership.CredentialKind::ALLis the onelist dispatch / storage / UI iterate.
CommunityRecord.credentials: BTreeMap<CredentialKind, Value>replaces the two
Optionfields.handle_credential_issueclassifies viaCredentialKind::from_credentialand inserts — no per-kind branching.Adding a kind is now one variant + its match arms; nothing in dispatch or UI
changes.
Config round-trip (no migration)
A deserialize-only
CommunityRecordShadow(#[serde(from = ...)]) foldspre-R19 flat
membership_credential/role_credentialfields into the map,with new-format keys winning. The shadow keys the map by
Stringon purpose:an unrecognised kind written by a newer build is dropped with a warning rather
than making the whole config unloadable.
CredentialKindserializes as itsconfig_keystring so it is a valid JSON object key.Router
No change needed: every credential kind shares one
credential-exchange/issuemessage type and DIDComm route, so a new kindcan't drift the route list.
Tests
credential_kind_registry_is_self_consistent—from_credential/from_config_keyround-trip for everyALLkind; unknowns →None.credential_issue_handles_every_registered_kind— dispatch stores + activatesevery registered kind generically (the acceptance criterion: a new kind
touches the registry only).
legacy_flat_credential_fields_load_into_registry— old configs load.typed_credentials_round_trip_and_tolerate_unknown— new format round-trips;unknown key dropped, not fatal.
Gate
cargo fmt --all,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace— all green. A code-reviewer pass on the diff found noblockers (config round-trip, map-key serialization, and dispatch equivalence
all verified).
🤖 Generated with Claude Code