Skip to content

Property.gui_match validator rejects explicit gui: null, breaking parse of real tenant responses #169

@be-ant

Description

@be-ant

Summary

ofsc.models.Property.gui_match is a field-validator that rejects any value not in the allowed-hints list — including None. Real OFSC tenants return gui: null for properties with no GUI hint, so AsyncOFSMetadata.get_properties() raises ValidationError on parse for any tenant that has such a property.

Missing gui (default-to-None) is accepted; only an explicit null in the response fails. The two paths should behave the same.

Affected versions

Confirmed in 2.25.0; the validator code is unchanged in earlier 2.x versions.

Reproduction

from ofsc.models import Property

# Real tenants return gui: null explicitly. This raises:
Property.model_validate({
    'label': 'foo',
    'name': 'Foo',
    'type': 'string',
    'gui': None,
    'translations': [{'language': 'en', 'name': 'Foo'}],
})
# ValidationError: 1 validation error for Property
# gui
#   Value error, None is not a valid GUI value

# Missing 'gui' (defaults to None) is accepted:
Property.model_validate({
    'label': 'foo', 'name': 'Foo', 'type': 'string',
    'translations': [{'language': 'en', 'name': 'Foo'}],
})
# OK; p.gui == None

Root cause (ofsc/models/metadata.py)

@field_validator("gui")
@classmethod
def gui_match(cls, v):
    if v not in [
        "text", "checkbox", "combobox", "radiogroup", "file",
        "signature", "image", "url", "phone", "email",
        "capture", "geo", "attachments",
    ]:
        raise ValueError(f"{v} is not a valid GUI value")
    return v

The field is declared gui: Optional[str] = NoneNone is a valid value at the type level — but the validator never short-circuits on it.

Proposed fix

@field_validator("gui")
@classmethod
def gui_match(cls, v):
    if v is None:
        return v
    if v not in [
        "text", "checkbox", "combobox", "radiogroup", "file",
        "signature", "image", "url", "phone", "email",
        "capture", "geo", "attachments",
    ]:
        raise ValueError(f"{v} is not a valid GUI value")
    return v

Happy to send a PR with the fix + a regression test that exercises both explicit-null and missing-key paths.

Workaround

For downstream consumers, the workaround is to subclass Property and override gui_match to permit None, and use a custom OFSResponseList[YourProperty] with metadata._get_paginated_list(...) instead of metadata.get_properties() (which hard-codes the upstream PropertyListResponse).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions