diff --git a/api/models/enums.py b/api/models/enums.py index ae05fa242be..d560dca35ac 100644 --- a/api/models/enums.py +++ b/api/models/enums.py @@ -214,6 +214,18 @@ class EndUserType(StrEnum): SERVICE_API = "service-api" TRIGGER = "trigger" + @classmethod + @override + def _missing_(cls, value): + # Legacy rows persisted the service-api type with an underscore before it + # was normalized to the hyphenated value. The + # `4f7b2c8d9a10_normalize_legacy_end_user_type` migration rewrites those + # rows, but tolerate the old value here as well so an unmigrated end user + # keeps loading instead of failing enum validation on every request. + if value == "service_api": + return cls.SERVICE_API + return super()._missing_(value) + class DocumentDocType(StrEnum): """Document doc_type classification""" diff --git a/api/tests/unit_tests/models/test_end_user_type.py b/api/tests/unit_tests/models/test_end_user_type.py index 55a4d35cbb9..222945814b4 100644 --- a/api/tests/unit_tests/models/test_end_user_type.py +++ b/api/tests/unit_tests/models/test_end_user_type.py @@ -2,6 +2,9 @@ import ast import inspect from pathlib import Path +import pytest +from sqlalchemy.engine.default import DefaultDialect + from models.enums import EndUserType from models.model import EndUser from models.types import EnumText @@ -24,6 +27,24 @@ def test_end_user_type_is_plain_persisted_value_enum(): assert not hasattr(EndUserType, "from_invoke_from") +def test_end_user_type_accepts_legacy_service_api_value(): + # Legacy rows stored the service-api type with an underscore before it was + # normalized to the hyphenated value; loading them must not raise. See #38201. + assert EndUserType("service_api") is EndUserType.SERVICE_API + assert EndUserType("service-api") is EndUserType.SERVICE_API + + +def test_end_user_type_still_rejects_unknown_values(): + with pytest.raises(ValueError): + EndUserType("not-a-real-end-user-type") + + +def test_enum_text_deserializes_legacy_service_api_value(): + # Exercises the actual column load path that raised for unmigrated rows. + column_type = EnumText(EndUserType) + assert column_type.process_result_value("service_api", DefaultDialect()) is EndUserType.SERVICE_API + + def test_end_user_service_creation_methods_accept_end_user_type(): assert inspect.signature(EndUserService.get_or_create_end_user_by_type).parameters["type"].annotation is EndUserType assert inspect.signature(EndUserService.create_end_user_batch).parameters["type"].annotation is EndUserType