Skip to content

fix: enforce uniqueItems on generated array fields - #59

Open
XiaolongZhang-TT wants to merge 1 commit into
Universal-Commerce-Protocol:mainfrom
XiaolongZhang-TT:fix/enforce-unique-items
Open

fix: enforce uniqueItems on generated array fields#59
XiaolongZhang-TT wants to merge 1 commit into
Universal-Commerce-Protocol:mainfrom
XiaolongZhang-TT:fix/enforce-unique-items

Conversation

@XiaolongZhang-TT

Copy link
Copy Markdown
Contributor

Description

datamodel-code-generator drops uniqueItems, so generated list fields accept duplicate entries in violation of the schema. Three UCP array properties declare uniqueItems: true at the 2026-04-08 release this SDK targets: context.eligibility, card_payment_instrument.brands, and identity_linking.required_claims.

This extends postprocess_models.py to collect array property names declared with uniqueItems and inject a field_validator(mode="after") into each generated class that declares a matching list field. The check uses equality (item in seen) rather than a set, so it holds for both hashable (str) and unhashable (Pydantic model) items. It mirrors the data-driven, idempotent approach already used for minProperties (#55), and complements the open contains work in #57 — the third constraint the generator drops.

Two generated models carry the affected list fields and gain the validator: Context.eligibility and Constraints.brands. required_claims has no generated typed field (ScopePolicy is extra="allow" free-form, so the field is an untyped extra key), so it is not enforceable and is skipped. Verified against the real release/2026-04-08 schemas that exactly these two models are touched on a fresh tree.

Note: like #57, this modifies postprocess_models.py, so it may need a rebase if #57 lands first.

Category (Required)

Please select one or more categories that apply to this change.

  • Core Protocol: Changes to the base communication layer, global context, or breaking refactors. (Requires Technical Council approval)
  • Governance/Contributing: Updates to GOVERNANCE.md, CONTRIBUTING.md, or CODEOWNERS. (Requires Governance Council approval)
  • Capability: New schemas (Discovery, Cart, etc.) or extensions. (Requires Maintainer approval)
  • Documentation: Updates to README, or documentations regarding schema or capabilities. (Requires Maintainer approval)
  • Infrastructure: CI/CD, Linters, or build scripts. (Requires DevOps Maintainer approval)
  • Maintenance: Version bumps, lockfile updates, or minor bug fixes. (Requires DevOps Maintainer approval)
  • SDK: Language-specific SDK updates and releases. (Requires DevOps Maintainer approval)
  • Samples / Conformance: Maintaining samples and the conformance suite. (Requires Maintainer approval)
  • UCP Schema: Changes to the ucp-schema tool (resolver, linter, validator). (Requires Maintainer approval)
  • Community Health (.github): Updates to templates, workflows, or org-level configs. (Requires DevOps Maintainer approval)

Related Issues

None. (Follow-up to the constraint-enforcement work in #55; complements the open contains enforcement in #57.)

Checklist

  • I have followed the Contributing Guide (including Conventional Commits title requirements and ! for breaking changes).
  • I have updated the documentation (if applicable).
  • My changes pass all local linting and formatting checks.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • (For Core/Capability) I have included/updated the relevant JSON schemas.
  • I have regenerated Python Pydantic models by running generate_models.sh under python_sdk.

Screenshots / Logs (if applicable)

The injected validator (e.g. Constraints.brands):

@field_validator("brands", mode="after")
def _enforce_unique_items_brands(cls, value):  # noqa: N805
    """JSON Schema uniqueItems: reject duplicate entries."""
    if value is None:
        return value
    seen = []
    for item in value:
        if item in seen:
            raise ValueError("Items must be unique (schema uniqueItems=true)")
        seen.append(item)
    return value

Before: Constraints(brands=["visa", "visa"]) was accepted. After: it raises ValidationError ("Items must be unique").

postprocess run reports: uniqueItems fields ['brands', 'eligibility', 'required_claims'] -> 2 module(s) patched (context.py, card_payment_instrument.py).

Full suite green as CI runs it (python -m unittest discover -s tests -p "test_*.py"): 35 passed, 0 failed. ruff (--ignore D,E501, matching the pre-commit hook), ruff format, and codespell are clean.

datamodel-code-generator drops `uniqueItems`, so generated list fields
accept duplicate entries in violation of the schema. Three UCP array
properties declare `uniqueItems: true` at 2026-04-08: context.eligibility,
card_payment_instrument.brands, and identity_linking.required_claims.

Extend postprocess_models.py to collect array property names declared with
`uniqueItems` and inject a `field_validator(mode="after")` into each
generated class that declares a matching list field. The check uses
equality (`item in seen`) so it holds for both hashable (str) and
unhashable (model) items. Mirrors the data-driven, idempotent approach used
for minProperties (Universal-Commerce-Protocol#55).

Two generated models carry the affected list fields and gain the
validator: Context.eligibility and Constraints.brands. required_claims has
no generated typed field (ScopePolicy is extra="allow" free-form), so it is
not enforceable and is skipped.

Add UniqueItemsInjectorTest (scan walks nested properties and ignores
non-arrays; injection targets only matching list fields, is idempotent, and
enforces uniqueness when exec'd) and UniqueItemsSemanticTest (Constraints
rejects duplicate brands, accepts unique/None).
@damaz91 damaz91 added the status:needs-triage Signal that the PR is ready for human triage label Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:needs-triage Signal that the PR is ready for human triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants