From 45f20e796ba43078c11ae5b98cf1cae7e3d9d3b4 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:59:32 +0000 Subject: [PATCH 1/5] [fern-generated] Update SDK Generated by Fern CLI Version: unknown Generators: - fernapi/fern-python-sdk: 5.0.8 --- .fern/metadata.json | 2 +- README.md | 1 + poetry.lock | 20 ++--- pyproject.toml | 2 +- src/merge/core/client_wrapper.py | 4 +- src/merge/core/unchecked_base_model.py | 8 +- src/merge/resources/accounting/__init__.py | 6 ++ .../resources/accounting/types/__init__.py | 6 ++ .../resources/accounting/types/sync_status.py | 7 ++ .../types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/ats/__init__.py | 6 ++ src/merge/resources/ats/types/__init__.py | 6 ++ src/merge/resources/ats/types/sync_status.py | 7 ++ .../ats/types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/calendar/__init__.py | 6 ++ .../resources/calendar/types/__init__.py | 6 ++ .../resources/calendar/types/sync_status.py | 7 ++ .../calendar/types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/chat/__init__.py | 6 ++ src/merge/resources/chat/types/__init__.py | 6 ++ src/merge/resources/chat/types/sync_status.py | 7 ++ .../chat/types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/crm/__init__.py | 6 ++ src/merge/resources/crm/types/__init__.py | 6 ++ src/merge/resources/crm/types/sync_status.py | 7 ++ .../crm/types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/email/__init__.py | 6 ++ src/merge/resources/email/types/__init__.py | 6 ++ .../resources/email/types/sync_status.py | 7 ++ .../email/types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/filestorage/__init__.py | 6 ++ .../resources/filestorage/types/__init__.py | 6 ++ .../filestorage/types/sync_status.py | 7 ++ .../types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/hris/__init__.py | 6 ++ src/merge/resources/hris/types/__init__.py | 6 ++ src/merge/resources/hris/types/sync_status.py | 7 ++ .../hris/types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/knowledgebase/__init__.py | 6 ++ .../resources/knowledgebase/types/__init__.py | 6 ++ .../knowledgebase/types/sync_status.py | 7 ++ .../types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ src/merge/resources/ticketing/__init__.py | 6 ++ .../resources/ticketing/types/__init__.py | 6 ++ .../resources/ticketing/types/sync_status.py | 7 ++ .../types/sync_status_reason_enum.py | 20 +++++ .../types/sync_status_sync_status_reason.py | 7 ++ .../custom/test_parsing_error_unreachable.py | 76 ------------------- .../test_pydantic_config_no_deprecation.py | 60 --------------- 58 files changed, 481 insertions(+), 152 deletions(-) create mode 100644 src/merge/resources/accounting/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/accounting/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/ats/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/ats/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/calendar/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/calendar/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/chat/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/chat/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/crm/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/crm/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/email/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/email/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/filestorage/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/filestorage/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/hris/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/hris/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/knowledgebase/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/knowledgebase/types/sync_status_sync_status_reason.py create mode 100644 src/merge/resources/ticketing/types/sync_status_reason_enum.py create mode 100644 src/merge/resources/ticketing/types/sync_status_sync_status_reason.py delete mode 100644 tests/custom/test_parsing_error_unreachable.py delete mode 100644 tests/custom/test_pydantic_config_no_deprecation.py diff --git a/.fern/metadata.json b/.fern/metadata.json index 301cea97..b13aa5cf 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -12,5 +12,5 @@ "use_pydantic_field_aliases": true } }, - "sdkVersion": "4.0.0" + "sdkVersion": "3.1.1" } \ No newline at end of file diff --git a/README.md b/README.md index af7c77d3..46e1d435 100644 --- a/README.md +++ b/README.md @@ -252,5 +252,6 @@ while response.next is not None: + diff --git a/poetry.lock b/poetry.lock index abdeb8ab..9875f0a4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "annotated-types" -version = "0.7.0" +version = "0.8.0" description = "Reusable constraint types to use with typing.Annotated" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, + {file = "annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0"}, + {file = "annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7"}, ] [[package]] @@ -43,13 +43,13 @@ files = [ [[package]] name = "certifi" -version = "2026.6.17" +version = "2026.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" files = [ - {file = "certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db"}, - {file = "certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432"}, + {file = "certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775"}, + {file = "certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55"}, ] [[package]] @@ -605,13 +605,13 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.9.0.20260518" +version = "2.9.0.20260716" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.10" files = [ - {file = "types_python_dateutil-2.9.0.20260518-py3-none-any.whl", hash = "sha256:d6a9c5bd0de61460c8fdef8ab2b400f956a1a1075cce08d4e2b4434e478c50b8"}, - {file = "types_python_dateutil-2.9.0.20260518.tar.gz", hash = "sha256:51f02dc03b61c7f6a07df45797d4dfe8a1aa47f0b7db9ad89f6fd3a1a70e1b51"}, + {file = "types_python_dateutil-2.9.0.20260716-py3-none-any.whl", hash = "sha256:1ae41d51a5c5f6bbeeb7f1f34df7086d55ff1605cf88d2ed71a7a276e1a7794e"}, + {file = "types_python_dateutil-2.9.0.20260716.tar.gz", hash = "sha256:1d55d1c3024bdb4861bb6a6622c9ec800c433d87bdc5b16fb84cdd0eed4ef2cb"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 968ecbff..9a1af071 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ dynamic = ["version"] [tool.poetry] name = "MergePythonClient" -version = "4.0.0" +version = "3.1.1" description = "" readme = "README.md" authors = [] diff --git a/src/merge/core/client_wrapper.py b/src/merge/core/client_wrapper.py index 20929ea0..0242d0d2 100644 --- a/src/merge/core/client_wrapper.py +++ b/src/merge/core/client_wrapper.py @@ -29,12 +29,12 @@ def get_headers(self) -> typing.Dict[str, str]: import platform headers: typing.Dict[str, str] = { - "User-Agent": "MergePythonClient/4.0.0", + "User-Agent": "MergePythonClient/3.1.1", "X-Fern-Language": "Python", "X-Fern-Runtime": f"python/{platform.python_version()}", "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", "X-Fern-SDK-Name": "MergePythonClient", - "X-Fern-SDK-Version": "4.0.0", + "X-Fern-SDK-Version": "3.1.1", **(self.get_custom_headers() or {}), } if self._account_token is not None: diff --git a/src/merge/core/unchecked_base_model.py b/src/merge/core/unchecked_base_model.py index 43879b80..67d7e3be 100644 --- a/src/merge/core/unchecked_base_model.py +++ b/src/merge/core/unchecked_base_model.py @@ -440,8 +440,12 @@ def construct_type( except Exception: return object_ - # MERGE CUSTOMIZATION: return raw enum values instead of coercing to enum - # members, to preserve forward-compatible behavior for customers. + if inspect.isclass(base_type) and issubclass(base_type, enum.Enum): + try: + return base_type(object_) + except (ValueError, KeyError): + return object_ + return object_ diff --git a/src/merge/resources/accounting/__init__.py b/src/merge/resources/accounting/__init__.py index 3515ccf4..58eda808 100644 --- a/src/merge/resources/accounting/__init__.py +++ b/src/merge/resources/accounting/__init__.py @@ -577,7 +577,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, TaxComponent, TaxComponentComponentType, TaxRate, @@ -1356,7 +1358,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "TaxComponent": ".types", "TaxComponentComponentType": ".types", "TaxRate": ".types", @@ -2109,7 +2113,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "TaxComponent", "TaxComponentComponentType", "TaxRate", diff --git a/src/merge/resources/accounting/types/__init__.py b/src/merge/resources/accounting/types/__init__.py index e13bb37f..cfc36ea3 100644 --- a/src/merge/resources/accounting/types/__init__.py +++ b/src/merge/resources/accounting/types/__init__.py @@ -586,7 +586,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .tax_component import TaxComponent from .tax_component_component_type import TaxComponentComponentType from .tax_rate import TaxRate @@ -1215,7 +1217,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "TaxComponent": ".tax_component", "TaxComponentComponentType": ".tax_component_component_type", "TaxRate": ".tax_rate", @@ -1866,7 +1870,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "TaxComponent", "TaxComponentComponentType", "TaxRate", diff --git a/src/merge/resources/accounting/types/sync_status.py b/src/merge/resources/accounting/types/sync_status.py index 07ab1dc2..4bb187e3 100644 --- a/src/merge/resources/accounting/types/sync_status.py +++ b/src/merge/resources/accounting/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/accounting/types/sync_status_reason_enum.py b/src/merge/resources/accounting/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/accounting/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/accounting/types/sync_status_sync_status_reason.py b/src/merge/resources/accounting/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/accounting/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/ats/__init__.py b/src/merge/resources/ats/__init__.py index 876110f3..b2de65a7 100644 --- a/src/merge/resources/ats/__init__.py +++ b/src/merge/resources/ats/__init__.py @@ -219,7 +219,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, Tag, Url, UrlRequest, @@ -536,7 +538,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "Tag": ".types", "Url": ".types", "UrlRequest": ".types", @@ -846,7 +850,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "Tag", "Url", "UrlRequest", diff --git a/src/merge/resources/ats/types/__init__.py b/src/merge/resources/ats/types/__init__.py index 1d4d2145..b4264be5 100644 --- a/src/merge/resources/ats/types/__init__.py +++ b/src/merge/resources/ats/types/__init__.py @@ -220,7 +220,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .tag import Tag from .url import Url from .url_request import UrlRequest @@ -445,7 +447,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "Tag": ".tag", "Url": ".url", "UrlRequest": ".url_request", @@ -694,7 +698,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "Tag", "Url", "UrlRequest", diff --git a/src/merge/resources/ats/types/sync_status.py b/src/merge/resources/ats/types/sync_status.py index 07ab1dc2..4bb187e3 100644 --- a/src/merge/resources/ats/types/sync_status.py +++ b/src/merge/resources/ats/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/ats/types/sync_status_reason_enum.py b/src/merge/resources/ats/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/ats/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/ats/types/sync_status_sync_status_reason.py b/src/merge/resources/ats/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/ats/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/calendar/__init__.py b/src/merge/resources/calendar/__init__.py index 21bd54bd..6ab3c1b2 100644 --- a/src/merge/resources/calendar/__init__.py +++ b/src/merge/resources/calendar/__init__.py @@ -114,7 +114,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, User, UserFieldMappings, ValidationProblemSource, @@ -275,7 +277,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "User": ".types", "UserFieldMappings": ".types", "ValidationProblemSource": ".types", @@ -446,7 +450,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "User", "UserFieldMappings", "ValidationProblemSource", diff --git a/src/merge/resources/calendar/types/__init__.py b/src/merge/resources/calendar/types/__init__.py index a45668f0..f52ecbd1 100644 --- a/src/merge/resources/calendar/types/__init__.py +++ b/src/merge/resources/calendar/types/__init__.py @@ -115,7 +115,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .user import User from .user_field_mappings import UserFieldMappings from .validation_problem_source import ValidationProblemSource @@ -229,7 +231,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "User": ".user", "UserFieldMappings": ".user_field_mappings", "ValidationProblemSource": ".validation_problem_source", @@ -367,7 +371,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "User", "UserFieldMappings", "ValidationProblemSource", diff --git a/src/merge/resources/calendar/types/sync_status.py b/src/merge/resources/calendar/types/sync_status.py index e8564068..e27689eb 100644 --- a/src/merge/resources/calendar/types/sync_status.py +++ b/src/merge/resources/calendar/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/calendar/types/sync_status_reason_enum.py b/src/merge/resources/calendar/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/calendar/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/calendar/types/sync_status_sync_status_reason.py b/src/merge/resources/calendar/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/calendar/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/chat/__init__.py b/src/merge/resources/chat/__init__.py index 5233cf52..3f79b315 100644 --- a/src/merge/resources/chat/__init__.py +++ b/src/merge/resources/chat/__init__.py @@ -98,7 +98,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, TypeEnum, User, UserFieldMappings, @@ -238,7 +240,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "TypeEnum": ".types", "User": ".types", "UserFieldMappings": ".types", @@ -391,7 +395,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "TypeEnum", "User", "UserFieldMappings", diff --git a/src/merge/resources/chat/types/__init__.py b/src/merge/resources/chat/types/__init__.py index 559764ef..5c8f8764 100644 --- a/src/merge/resources/chat/types/__init__.py +++ b/src/merge/resources/chat/types/__init__.py @@ -99,7 +99,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .type_enum import TypeEnum from .user import User from .user_field_mappings import UserFieldMappings @@ -199,7 +201,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "TypeEnum": ".type_enum", "User": ".user", "UserFieldMappings": ".user_field_mappings", @@ -323,7 +327,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "TypeEnum", "User", "UserFieldMappings", diff --git a/src/merge/resources/chat/types/sync_status.py b/src/merge/resources/chat/types/sync_status.py index e8564068..e27689eb 100644 --- a/src/merge/resources/chat/types/sync_status.py +++ b/src/merge/resources/chat/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/chat/types/sync_status_reason_enum.py b/src/merge/resources/chat/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/chat/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/chat/types/sync_status_sync_status_reason.py b/src/merge/resources/chat/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/chat/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/crm/__init__.py b/src/merge/resources/crm/__init__.py index 7381a0d1..41ea5b4d 100644 --- a/src/merge/resources/crm/__init__.py +++ b/src/merge/resources/crm/__init__.py @@ -230,7 +230,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, Task, TaskAccount, TaskContact, @@ -539,7 +541,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "Task": ".types", "TaskAccount": ".types", "TaskContact": ".types", @@ -854,7 +858,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "Task", "TaskAccount", "TaskContact", diff --git a/src/merge/resources/crm/types/__init__.py b/src/merge/resources/crm/types/__init__.py index 2f1acbbb..a951896b 100644 --- a/src/merge/resources/crm/types/__init__.py +++ b/src/merge/resources/crm/types/__init__.py @@ -235,7 +235,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .task import Task from .task_account import TaskAccount from .task_contact import TaskContact @@ -478,7 +480,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "Task": ".task", "TaskAccount": ".task_account", "TaskContact": ".task_contact", @@ -745,7 +749,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "Task", "TaskAccount", "TaskContact", diff --git a/src/merge/resources/crm/types/sync_status.py b/src/merge/resources/crm/types/sync_status.py index 07ab1dc2..4bb187e3 100644 --- a/src/merge/resources/crm/types/sync_status.py +++ b/src/merge/resources/crm/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/crm/types/sync_status_reason_enum.py b/src/merge/resources/crm/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/crm/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/crm/types/sync_status_sync_status_reason.py b/src/merge/resources/crm/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/crm/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/email/__init__.py b/src/merge/resources/email/__init__.py index 371f7209..1a5536ab 100644 --- a/src/merge/resources/email/__init__.py +++ b/src/merge/resources/email/__init__.py @@ -103,7 +103,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, User, UserFieldMappings, UserType, @@ -266,7 +268,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "User": ".types", "UserFieldMappings": ".types", "UserType": ".types", @@ -435,7 +439,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "User", "UserFieldMappings", "UserType", diff --git a/src/merge/resources/email/types/__init__.py b/src/merge/resources/email/types/__init__.py index f0dd08c1..28d4af91 100644 --- a/src/merge/resources/email/types/__init__.py +++ b/src/merge/resources/email/types/__init__.py @@ -104,7 +104,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .user import User from .user_field_mappings import UserFieldMappings from .user_type import UserType @@ -209,7 +211,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "User": ".user", "UserFieldMappings": ".user_field_mappings", "UserType": ".user_type", @@ -338,7 +342,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "User", "UserFieldMappings", "UserType", diff --git a/src/merge/resources/email/types/sync_status.py b/src/merge/resources/email/types/sync_status.py index e8564068..e27689eb 100644 --- a/src/merge/resources/email/types/sync_status.py +++ b/src/merge/resources/email/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/email/types/sync_status_reason_enum.py b/src/merge/resources/email/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/email/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/email/types/sync_status_sync_status_reason.py b/src/merge/resources/email/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/email/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/filestorage/__init__.py b/src/merge/resources/filestorage/__init__.py index 7b490ba4..467c6199 100644 --- a/src/merge/resources/filestorage/__init__.py +++ b/src/merge/resources/filestorage/__init__.py @@ -123,7 +123,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, TypeEnum, User, ValidationProblemSource, @@ -293,7 +295,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "TypeEnum": ".types", "User": ".types", "ValidationProblemSource": ".types", @@ -473,7 +477,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "TypeEnum", "User", "ValidationProblemSource", diff --git a/src/merge/resources/filestorage/types/__init__.py b/src/merge/resources/filestorage/types/__init__.py index 7245d01c..a59f2e69 100644 --- a/src/merge/resources/filestorage/types/__init__.py +++ b/src/merge/resources/filestorage/types/__init__.py @@ -124,7 +124,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .type_enum import TypeEnum from .user import User from .validation_problem_source import ValidationProblemSource @@ -247,7 +249,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "TypeEnum": ".type_enum", "User": ".user", "ValidationProblemSource": ".validation_problem_source", @@ -394,7 +398,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "TypeEnum", "User", "ValidationProblemSource", diff --git a/src/merge/resources/filestorage/types/sync_status.py b/src/merge/resources/filestorage/types/sync_status.py index 07ab1dc2..4bb187e3 100644 --- a/src/merge/resources/filestorage/types/sync_status.py +++ b/src/merge/resources/filestorage/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/filestorage/types/sync_status_reason_enum.py b/src/merge/resources/filestorage/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/filestorage/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/filestorage/types/sync_status_sync_status_reason.py b/src/merge/resources/filestorage/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/filestorage/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/hris/__init__.py b/src/merge/resources/hris/__init__.py index 71e1b0f7..2e23b427 100644 --- a/src/merge/resources/hris/__init__.py +++ b/src/merge/resources/hris/__init__.py @@ -179,7 +179,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, Tax, Team, TeamParentTeam, @@ -494,7 +496,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "Tax": ".types", "Team": ".types", "TeamParentTeam": ".types", @@ -797,7 +801,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "Tax", "Team", "TeamParentTeam", diff --git a/src/merge/resources/hris/types/__init__.py b/src/merge/resources/hris/types/__init__.py index a29c5710..6a79dd4d 100644 --- a/src/merge/resources/hris/types/__init__.py +++ b/src/merge/resources/hris/types/__init__.py @@ -180,7 +180,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .tax import Tax from .team import Team from .team_parent_team import TeamParentTeam @@ -383,7 +385,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "Tax": ".tax", "Team": ".team", "TeamParentTeam": ".team_parent_team", @@ -610,7 +614,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "Tax", "Team", "TeamParentTeam", diff --git a/src/merge/resources/hris/types/sync_status.py b/src/merge/resources/hris/types/sync_status.py index 07ab1dc2..4bb187e3 100644 --- a/src/merge/resources/hris/types/sync_status.py +++ b/src/merge/resources/hris/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/hris/types/sync_status_reason_enum.py b/src/merge/resources/hris/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/hris/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/hris/types/sync_status_sync_status_reason.py b/src/merge/resources/hris/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/hris/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/knowledgebase/__init__.py b/src/merge/resources/knowledgebase/__init__.py index b58d5025..fc18d6b9 100644 --- a/src/merge/resources/knowledgebase/__init__.py +++ b/src/merge/resources/knowledgebase/__init__.py @@ -120,7 +120,9 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, SyncStatusStatus, + SyncStatusSyncStatusReason, User, UserFieldMappings, ValidationProblemSource, @@ -292,7 +294,9 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", "SyncStatusStatus": ".types", + "SyncStatusSyncStatusReason": ".types", "User": ".types", "UserFieldMappings": ".types", "ValidationProblemSource": ".types", @@ -472,7 +476,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "User", "UserFieldMappings", "ValidationProblemSource", diff --git a/src/merge/resources/knowledgebase/types/__init__.py b/src/merge/resources/knowledgebase/types/__init__.py index bea61591..df544251 100644 --- a/src/merge/resources/knowledgebase/types/__init__.py +++ b/src/merge/resources/knowledgebase/types/__init__.py @@ -121,7 +121,9 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum from .sync_status_status import SyncStatusStatus + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .user import User from .user_field_mappings import UserFieldMappings from .validation_problem_source import ValidationProblemSource @@ -242,7 +244,9 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", "SyncStatusStatus": ".sync_status_status", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "User": ".user", "UserFieldMappings": ".user_field_mappings", "ValidationProblemSource": ".validation_problem_source", @@ -387,7 +391,9 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", "SyncStatusStatus", + "SyncStatusSyncStatusReason", "User", "UserFieldMappings", "ValidationProblemSource", diff --git a/src/merge/resources/knowledgebase/types/sync_status.py b/src/merge/resources/knowledgebase/types/sync_status.py index e8564068..e27689eb 100644 --- a/src/merge/resources/knowledgebase/types/sync_status.py +++ b/src/merge/resources/knowledgebase/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .sync_status_last_sync_result import SyncStatusLastSyncResult from .sync_status_status import SyncStatusStatus +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: SyncStatusStatus + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/knowledgebase/types/sync_status_reason_enum.py b/src/merge/resources/knowledgebase/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/knowledgebase/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/knowledgebase/types/sync_status_sync_status_reason.py b/src/merge/resources/knowledgebase/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/knowledgebase/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/src/merge/resources/ticketing/__init__.py b/src/merge/resources/ticketing/__init__.py index d85e82e1..36845f60 100644 --- a/src/merge/resources/ticketing/__init__.py +++ b/src/merge/resources/ticketing/__init__.py @@ -151,6 +151,8 @@ StatusFd5Enum, SyncStatus, SyncStatusLastSyncResult, + SyncStatusReasonEnum, + SyncStatusSyncStatusReason, Tag, Team, Ticket, @@ -404,6 +406,8 @@ "StatusFd5Enum": ".types", "SyncStatus": ".types", "SyncStatusLastSyncResult": ".types", + "SyncStatusReasonEnum": ".types", + "SyncStatusSyncStatusReason": ".types", "Tag": ".types", "Team": ".types", "Ticket": ".types", @@ -667,6 +671,8 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", + "SyncStatusSyncStatusReason", "Tag", "Team", "Ticket", diff --git a/src/merge/resources/ticketing/types/__init__.py b/src/merge/resources/ticketing/types/__init__.py index c2cf2ca8..8bc7434d 100644 --- a/src/merge/resources/ticketing/types/__init__.py +++ b/src/merge/resources/ticketing/types/__init__.py @@ -152,6 +152,8 @@ from .status_fd_5_enum import StatusFd5Enum from .sync_status import SyncStatus from .sync_status_last_sync_result import SyncStatusLastSyncResult + from .sync_status_reason_enum import SyncStatusReasonEnum + from .sync_status_sync_status_reason import SyncStatusSyncStatusReason from .tag import Tag from .team import Team from .ticket import Ticket @@ -339,6 +341,8 @@ "StatusFd5Enum": ".status_fd_5_enum", "SyncStatus": ".sync_status", "SyncStatusLastSyncResult": ".sync_status_last_sync_result", + "SyncStatusReasonEnum": ".sync_status_reason_enum", + "SyncStatusSyncStatusReason": ".sync_status_sync_status_reason", "Tag": ".tag", "Team": ".team", "Ticket": ".ticket", @@ -550,6 +554,8 @@ def __dir__(): "StatusFd5Enum", "SyncStatus", "SyncStatusLastSyncResult", + "SyncStatusReasonEnum", + "SyncStatusSyncStatusReason", "Tag", "Team", "Ticket", diff --git a/src/merge/resources/ticketing/types/sync_status.py b/src/merge/resources/ticketing/types/sync_status.py index 4a628c4f..af29a5a1 100644 --- a/src/merge/resources/ticketing/types/sync_status.py +++ b/src/merge/resources/ticketing/types/sync_status.py @@ -9,6 +9,7 @@ from .selective_sync_configurations_usage_enum import SelectiveSyncConfigurationsUsageEnum from .status_fd_5_enum import StatusFd5Enum from .sync_status_last_sync_result import SyncStatusLastSyncResult +from .sync_status_sync_status_reason import SyncStatusSyncStatusReason class SyncStatus(UncheckedBaseModel): @@ -27,7 +28,13 @@ class SyncStatus(UncheckedBaseModel): next_sync_start: typing.Optional[dt.datetime] = None last_sync_result: typing.Optional[SyncStatusLastSyncResult] = None last_sync_finished: typing.Optional[dt.datetime] = None + data_fresh_as_of: typing.Optional[dt.datetime] = None status: StatusFd5Enum + sync_status_reason: typing.Optional[SyncStatusSyncStatusReason] = pydantic.Field(default=None) + """ + The reason a model with a `SYNCING` status is temporarily not progressing. `RATE_LIMITED` indicates the model is paused to respect the data provider's rate limits. `WAITING_ON_OTHER_MODELS` indicates the model is waiting for other models to finish syncing. + """ + is_initial_sync: bool selective_sync_configurations_usage: typing.Optional[SelectiveSyncConfigurationsUsageEnum] = None diff --git a/src/merge/resources/ticketing/types/sync_status_reason_enum.py b/src/merge/resources/ticketing/types/sync_status_reason_enum.py new file mode 100644 index 00000000..f1da56fb --- /dev/null +++ b/src/merge/resources/ticketing/types/sync_status_reason_enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class SyncStatusReasonEnum(enum.StrEnum): + RATE_LIMITED = "RATE_LIMITED" + WAITING_ON_OTHER_MODELS = "WAITING_ON_OTHER_MODELS" + + def visit( + self, rate_limited: typing.Callable[[], T_Result], waiting_on_other_models: typing.Callable[[], T_Result] + ) -> T_Result: + if self is SyncStatusReasonEnum.RATE_LIMITED: + return rate_limited() + if self is SyncStatusReasonEnum.WAITING_ON_OTHER_MODELS: + return waiting_on_other_models() diff --git a/src/merge/resources/ticketing/types/sync_status_sync_status_reason.py b/src/merge/resources/ticketing/types/sync_status_sync_status_reason.py new file mode 100644 index 00000000..56da308e --- /dev/null +++ b/src/merge/resources/ticketing/types/sync_status_sync_status_reason.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .sync_status_reason_enum import SyncStatusReasonEnum + +SyncStatusSyncStatusReason = typing.Union[SyncStatusReasonEnum, str] diff --git a/tests/custom/test_parsing_error_unreachable.py b/tests/custom/test_parsing_error_unreachable.py deleted file mode 100644 index 6e49e5f5..00000000 --- a/tests/custom/test_parsing_error_unreachable.py +++ /dev/null @@ -1,76 +0,0 @@ -import httpx -import pydantic -import pytest - -from merge import Merge -from merge.core import ApiError, ParsingError -from merge.core.http_response import HttpResponse -from merge.core.unchecked_base_model import construct_type -from merge.resources.accounting.types.account import Account -from merge.resources.accounting.types.paginated_account_list import PaginatedAccountList - - -MALFORMED_ACCOUNT = { - "id": 12345, - "name": 999, - "current_balance": "not-a-number", - "created_at": "definitely-not-a-datetime", - "status": {"unexpected": "shape"}, - "company": ["wrong", "container"], - "classification": 3.14, - "totally_unexpected_key": {"nested": [1, 2, 3]}, -} - -MALFORMED_PAGE = { - "next": 42, - "previous": {"not": "a-string"}, - "results": [MALFORMED_ACCOUNT, "a-bare-string-not-an-object", 777], -} - - -def _client_with_response(response: httpx.Response) -> Merge: - def handler(request: httpx.Request) -> httpx.Response: - return response - - httpx_client = httpx.Client(transport=httpx.MockTransport(handler)) - return Merge(account_token="test-token", api_key="test-key", httpx_client=httpx_client) - - -def test_malformed_input_is_genuinely_schema_violating(): - with pytest.raises(pydantic.ValidationError): - Account.model_validate(MALFORMED_ACCOUNT) - - -def test_parsing_error_is_not_in_the_api_error_hierarchy(): - assert not issubclass(ParsingError, ApiError) - assert not issubclass(ApiError, ParsingError) - - -def test_construct_type_does_not_raise_on_malformed_account(): - result = construct_type(type_=Account, object_=MALFORMED_ACCOUNT) - assert isinstance(result, Account) - - -def test_construct_type_does_not_raise_on_malformed_paginated_list(): - result = construct_type(type_=PaginatedAccountList, object_=MALFORMED_PAGE) - assert isinstance(result, PaginatedAccountList) - - -def test_success_path_with_schema_violating_body_never_raises_parsing_error(): - client = _client_with_response(httpx.Response(200, json=MALFORMED_PAGE)) - result = client.accounting.accounts.list() - assert isinstance(result, PaginatedAccountList) - - -def test_raw_success_path_returns_http_response_not_parsing_error(): - client = _client_with_response(httpx.Response(200, json=MALFORMED_PAGE)) - raw = client.accounting.accounts.with_raw_response.list() - assert isinstance(raw, HttpResponse) - - -def test_error_path_raises_api_error_not_parsing_error(): - client = _client_with_response(httpx.Response(400, text="gateway error")) - with pytest.raises(ApiError) as exc_info: - client.accounting.accounts.list() - assert not isinstance(exc_info.value, ParsingError) - assert exc_info.value.status_code == 400 diff --git a/tests/custom/test_pydantic_config_no_deprecation.py b/tests/custom/test_pydantic_config_no_deprecation.py deleted file mode 100644 index d97af000..00000000 --- a/tests/custom/test_pydantic_config_no_deprecation.py +++ /dev/null @@ -1,60 +0,0 @@ -import warnings - -import pydantic -import pytest - -from merge.core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from merge.core.unchecked_base_model import UncheckedBaseModel -from merge.resources.accounting.types.account import Account -from merge.resources.accounting.types.field_mapping_api_instance_response import ( - FieldMappingApiInstanceResponse, -) - -pytestmark = pytest.mark.skipif(not IS_PYDANTIC_V2, reason="config deprecation only applies on pydantic v2") - -_DEPRECATION_MARKERS = ( - "class-based `config`", - "allow_population_by_field_name", - "smart_union", - "Valid config keys have changed", -) - - -def _pydantic_config_warnings(records): - hits = [] - for r in records: - if not issubclass(r.category, (DeprecationWarning, UserWarning, PendingDeprecationWarning)): - continue - text = str(r.message) - if any(marker in text for marker in _DEPRECATION_MARKERS): - hits.append(text) - return hits - - -def test_defining_a_subclass_emits_no_config_deprecation_warnings(): - with warnings.catch_warnings(record=True) as records: - warnings.simplefilter("always") - - class _Probe(UncheckedBaseModel): - name: str = pydantic.Field(default="", alias="Name") - - _Probe(Name="probe") - - hits = _pydantic_config_warnings(records) - assert hits == [], f"deprecated pydantic config resurfaced (issue #117): {hits}" - - -def test_instantiating_real_models_emits_no_config_deprecation_warnings(): - with warnings.catch_warnings(record=True) as records: - warnings.simplefilter("always") - Account(name="Cash", id="123") - FieldMappingApiInstanceResponse(account=[]) - - hits = _pydantic_config_warnings(records) - assert hits == [], f"deprecated pydantic config resurfaced (issue #117): {hits}" - - -def test_base_model_config_has_no_deprecated_v1_keys(): - config = UniversalBaseModel.model_config - assert "smart_union" not in config - assert "allow_population_by_field_name" not in config From 83f0fb13e03d2313eaba1886bf9adb9ae552b9a3 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:59:32 +0000 Subject: [PATCH 2/5] [fern-replay] Applied customizations Patches with unresolved conflicts (1): - patch-09841618: update version to 3.1.0 (#198) Run `fern-replay resolve` to apply these customizations. --- .fern/replay.lock | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.fern/replay.lock b/.fern/replay.lock index 30ee9c54..6ff0d684 100644 --- a/.fern/replay.lock +++ b/.fern/replay.lock @@ -12,7 +12,13 @@ generations: cli_version: unknown generator_versions: fernapi/fern-python-sdk: 5.0.8 -current_generation: 3f73767305ee9dc15a905e160650c32b0cb83a27 + - commit_sha: 04412c93c00102a006063feaaa478933aafd1bdb + tree_hash: 1427586cdd8a9ebe73e69b2d603a496d383ed786 + timestamp: 2026-07-24T14:59:29.036Z + cli_version: unknown + generator_versions: + fernapi/fern-python-sdk: 5.0.8 +current_generation: 04412c93c00102a006063feaaa478933aafd1bdb patches: - id: patch-09841618 content_hash: sha256:5da05043df04207e1c3dad25d3c390a26cfc9f23b67f46f105aa367c41393ba5 From 73ffd8d910763dbae0a87373ff804965ffccc65f Mon Sep 17 00:00:00 2001 From: Ashlee Kupor Date: Fri, 24 Jul 2026 11:41:54 -0400 Subject: [PATCH 3/5] Preserve raw-string enum forward-compat behavior fern-python-sdk 5.x added an enum.Enum coercion branch in construct_type that turns known enum values into enum members. This changes str()/type()/repr() for consumers (e.g. str(member) == "PayPeriodEnum.HOUR" under the 3.10 StrEnum shim), a breaking change for customers. Revert to returning the raw value, matching prior behavior and the forward-compat tests in tests/custom/test_enum_forward_compat_response.py. Also add src/merge/core/unchecked_base_model.py to .fernignore so this customization is no longer overwritten on future SDK regenerations. Co-Authored-By: Claude Opus 4.8 (1M context) --- .fernignore | 3 +++ src/merge/core/unchecked_base_model.py | 8 ++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.fernignore b/.fernignore index 40dcfa92..2107be21 100644 --- a/.fernignore +++ b/.fernignore @@ -10,6 +10,9 @@ LICENSE.md src/merge/resources/accounting/types/currency_enum.py src/merge/core/query_encoder.py +# Preserve raw-string enum forward-compat behavior (see construct_type). +src/merge/core/unchecked_base_model.py + tests/integration/test_filestorage.py tests/integration/test_knowledgebase.py tests/integration/test_email.py diff --git a/src/merge/core/unchecked_base_model.py b/src/merge/core/unchecked_base_model.py index 67d7e3be..43879b80 100644 --- a/src/merge/core/unchecked_base_model.py +++ b/src/merge/core/unchecked_base_model.py @@ -440,12 +440,8 @@ def construct_type( except Exception: return object_ - if inspect.isclass(base_type) and issubclass(base_type, enum.Enum): - try: - return base_type(object_) - except (ValueError, KeyError): - return object_ - + # MERGE CUSTOMIZATION: return raw enum values instead of coercing to enum + # members, to preserve forward-compatible behavior for customers. return object_ From d0870880f7b5a0ce7c616cf23f55c154e7e45fe0 Mon Sep 17 00:00:00 2001 From: Ashlee Kupor Date: Fri, 24 Jul 2026 11:45:24 -0400 Subject: [PATCH 4/5] update version to 4.1.0 Additive release: SyncStatus gains data_fresh_as_of and sync_status_reason (new optional response fields) across all categories. Minor bump per breaking-changes analysis (0 breaking changes, new features added). Co-Authored-By: Claude Opus 4.8 (1M context) --- .fern/metadata.json | 2 +- pyproject.toml | 2 +- src/merge/core/client_wrapper.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.fern/metadata.json b/.fern/metadata.json index b13aa5cf..ac469c3a 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -12,5 +12,5 @@ "use_pydantic_field_aliases": true } }, - "sdkVersion": "3.1.1" + "sdkVersion": "4.1.0" } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9a1af071..a0df13d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ dynamic = ["version"] [tool.poetry] name = "MergePythonClient" -version = "3.1.1" +version = "4.1.0" description = "" readme = "README.md" authors = [] diff --git a/src/merge/core/client_wrapper.py b/src/merge/core/client_wrapper.py index 0242d0d2..a3b22336 100644 --- a/src/merge/core/client_wrapper.py +++ b/src/merge/core/client_wrapper.py @@ -29,12 +29,12 @@ def get_headers(self) -> typing.Dict[str, str]: import platform headers: typing.Dict[str, str] = { - "User-Agent": "MergePythonClient/3.1.1", + "User-Agent": "MergePythonClient/4.1.0", "X-Fern-Language": "Python", "X-Fern-Runtime": f"python/{platform.python_version()}", "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", "X-Fern-SDK-Name": "MergePythonClient", - "X-Fern-SDK-Version": "3.1.1", + "X-Fern-SDK-Version": "4.1.0", **(self.get_custom_headers() or {}), } if self._account_token is not None: From 081767225d2cb7200dbd60df49ed6de91c8acd8a Mon Sep 17 00:00:00 2001 From: Ashlee Kupor Date: Fri, 24 Jul 2026 12:21:21 -0400 Subject: [PATCH 5/5] Restore custom tests dropped by regen; ignore them going forward The regeneration deleted tests/custom/test_parsing_error_unreachable.py and tests/custom/test_pydantic_config_no_deprecation.py because they weren't in .fernignore. Restore both from main and add them to .fernignore so they survive future regenerations. Also revert a cosmetic trailing-blank-line churn in README.md. Co-Authored-By: Claude Opus 4.8 (1M context) --- .fernignore | 2 + README.md | 1 - .../custom/test_parsing_error_unreachable.py | 76 +++++++++++++++++++ .../test_pydantic_config_no_deprecation.py | 60 +++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 tests/custom/test_parsing_error_unreachable.py create mode 100644 tests/custom/test_pydantic_config_no_deprecation.py diff --git a/.fernignore b/.fernignore index 2107be21..b8bf6e9a 100644 --- a/.fernignore +++ b/.fernignore @@ -26,6 +26,8 @@ tests/custom/test_error_response_handling.py tests/custom/test_query_encoder_edges.py tests/custom/test_enum_forward_compat_response.py tests/custom/test_enum_forward_compat_query.py +tests/custom/test_parsing_error_unreachable.py +tests/custom/test_pydantic_config_no_deprecation.py .fern/replay.lock .fern/replay.yml .gitattributes diff --git a/README.md b/README.md index 46e1d435..af7c77d3 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,5 @@ while response.next is not None: - diff --git a/tests/custom/test_parsing_error_unreachable.py b/tests/custom/test_parsing_error_unreachable.py new file mode 100644 index 00000000..6e49e5f5 --- /dev/null +++ b/tests/custom/test_parsing_error_unreachable.py @@ -0,0 +1,76 @@ +import httpx +import pydantic +import pytest + +from merge import Merge +from merge.core import ApiError, ParsingError +from merge.core.http_response import HttpResponse +from merge.core.unchecked_base_model import construct_type +from merge.resources.accounting.types.account import Account +from merge.resources.accounting.types.paginated_account_list import PaginatedAccountList + + +MALFORMED_ACCOUNT = { + "id": 12345, + "name": 999, + "current_balance": "not-a-number", + "created_at": "definitely-not-a-datetime", + "status": {"unexpected": "shape"}, + "company": ["wrong", "container"], + "classification": 3.14, + "totally_unexpected_key": {"nested": [1, 2, 3]}, +} + +MALFORMED_PAGE = { + "next": 42, + "previous": {"not": "a-string"}, + "results": [MALFORMED_ACCOUNT, "a-bare-string-not-an-object", 777], +} + + +def _client_with_response(response: httpx.Response) -> Merge: + def handler(request: httpx.Request) -> httpx.Response: + return response + + httpx_client = httpx.Client(transport=httpx.MockTransport(handler)) + return Merge(account_token="test-token", api_key="test-key", httpx_client=httpx_client) + + +def test_malformed_input_is_genuinely_schema_violating(): + with pytest.raises(pydantic.ValidationError): + Account.model_validate(MALFORMED_ACCOUNT) + + +def test_parsing_error_is_not_in_the_api_error_hierarchy(): + assert not issubclass(ParsingError, ApiError) + assert not issubclass(ApiError, ParsingError) + + +def test_construct_type_does_not_raise_on_malformed_account(): + result = construct_type(type_=Account, object_=MALFORMED_ACCOUNT) + assert isinstance(result, Account) + + +def test_construct_type_does_not_raise_on_malformed_paginated_list(): + result = construct_type(type_=PaginatedAccountList, object_=MALFORMED_PAGE) + assert isinstance(result, PaginatedAccountList) + + +def test_success_path_with_schema_violating_body_never_raises_parsing_error(): + client = _client_with_response(httpx.Response(200, json=MALFORMED_PAGE)) + result = client.accounting.accounts.list() + assert isinstance(result, PaginatedAccountList) + + +def test_raw_success_path_returns_http_response_not_parsing_error(): + client = _client_with_response(httpx.Response(200, json=MALFORMED_PAGE)) + raw = client.accounting.accounts.with_raw_response.list() + assert isinstance(raw, HttpResponse) + + +def test_error_path_raises_api_error_not_parsing_error(): + client = _client_with_response(httpx.Response(400, text="gateway error")) + with pytest.raises(ApiError) as exc_info: + client.accounting.accounts.list() + assert not isinstance(exc_info.value, ParsingError) + assert exc_info.value.status_code == 400 diff --git a/tests/custom/test_pydantic_config_no_deprecation.py b/tests/custom/test_pydantic_config_no_deprecation.py new file mode 100644 index 00000000..d97af000 --- /dev/null +++ b/tests/custom/test_pydantic_config_no_deprecation.py @@ -0,0 +1,60 @@ +import warnings + +import pydantic +import pytest + +from merge.core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from merge.core.unchecked_base_model import UncheckedBaseModel +from merge.resources.accounting.types.account import Account +from merge.resources.accounting.types.field_mapping_api_instance_response import ( + FieldMappingApiInstanceResponse, +) + +pytestmark = pytest.mark.skipif(not IS_PYDANTIC_V2, reason="config deprecation only applies on pydantic v2") + +_DEPRECATION_MARKERS = ( + "class-based `config`", + "allow_population_by_field_name", + "smart_union", + "Valid config keys have changed", +) + + +def _pydantic_config_warnings(records): + hits = [] + for r in records: + if not issubclass(r.category, (DeprecationWarning, UserWarning, PendingDeprecationWarning)): + continue + text = str(r.message) + if any(marker in text for marker in _DEPRECATION_MARKERS): + hits.append(text) + return hits + + +def test_defining_a_subclass_emits_no_config_deprecation_warnings(): + with warnings.catch_warnings(record=True) as records: + warnings.simplefilter("always") + + class _Probe(UncheckedBaseModel): + name: str = pydantic.Field(default="", alias="Name") + + _Probe(Name="probe") + + hits = _pydantic_config_warnings(records) + assert hits == [], f"deprecated pydantic config resurfaced (issue #117): {hits}" + + +def test_instantiating_real_models_emits_no_config_deprecation_warnings(): + with warnings.catch_warnings(record=True) as records: + warnings.simplefilter("always") + Account(name="Cash", id="123") + FieldMappingApiInstanceResponse(account=[]) + + hits = _pydantic_config_warnings(records) + assert hits == [], f"deprecated pydantic config resurfaced (issue #117): {hits}" + + +def test_base_model_config_has_no_deprecated_v1_keys(): + config = UniversalBaseModel.model_config + assert "smart_union" not in config + assert "allow_population_by_field_name" not in config