Describe the bug
LIF's data model is permissive ("no loss" — most values stored as JSON strings), but a field's MDR DataType maps it to a strict GraphQL/Python type (int/bool/float/date). When the stored value doesn't match the declared type, the GraphQL resolver mishandles it.
components/lif/openapi_to_graphql/type_factory.py — dict_to_dataclass (~L560-600) coerces lists, nested objects, date, and datetime, but has no branch for int/bool/float. So a value like "Yes", "N/A", or "50" arriving for a field declared boolean/integer/number:
- passes through uncoerced → Strawberry serialization sees a
str where bool/int/float is annotated → type mismatch, and
- the surrounding
try/except sets the field to None and logs a warning (silent data loss; the code even carries a TODO: should really be more strict).
This is the value/type analogue of the name problem (#1011): permissive source data vs. a strict typed consumer.
Severity: runtime — wrong/empty values returned to the frontend, no error surfaced to the caller.
Fix direction (ties to the two entry points):
- Data entry point: validate ingested data against its assigned schema + sanity checks (a typed field shouldn't accept a non-conforming string silently).
- Resolver robustness: add explicit
int/bool/float coercion in dict_to_dataclass (mirror the date/datetime handling), and decide a policy for non-coercible values (preserve-as-string vs. null-with-signal) rather than silent None.
Related: #1011/#1012 (name analogue), #1014 (naming/validation ADR — consider a sibling decision for type-vs-data), data-model-rules.md ("primitive JSON / strings for flexibility").
Describe the bug
LIF's data model is permissive ("no loss" — most values stored as JSON strings), but a field's MDR
DataTypemaps it to a strict GraphQL/Python type (int/bool/float/date). When the stored value doesn't match the declared type, the GraphQL resolver mishandles it.components/lif/openapi_to_graphql/type_factory.py—dict_to_dataclass(~L560-600) coerces lists, nested objects,date, anddatetime, but has no branch forint/bool/float. So a value like"Yes","N/A", or"50"arriving for a field declaredboolean/integer/number:strwherebool/int/floatis annotated → type mismatch, andtry/exceptsets the field toNoneand logs a warning (silent data loss; the code even carries aTODO: should really be more strict).This is the value/type analogue of the name problem (#1011): permissive source data vs. a strict typed consumer.
Severity: runtime — wrong/empty values returned to the frontend, no error surfaced to the caller.
Fix direction (ties to the two entry points):
int/bool/floatcoercion indict_to_dataclass(mirror the date/datetime handling), and decide a policy for non-coercible values (preserve-as-string vs. null-with-signal) rather than silentNone.Related: #1011/#1012 (name analogue), #1014 (naming/validation ADR — consider a sibling decision for type-vs-data), data-model-rules.md ("primitive JSON / strings for flexibility").