diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1ce85403..7ad2c0bf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.20.5" + ".": "0.20.6" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 72c3e9f8..70538c9e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 35 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch-a14d8915465614a275f05ccb91513576fab0525b488cb878dbe3d692f5b2349f.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch-55ff507660a4f8b4a1809ca01861098c7bc72e8e6ea038fb3aa8c0a3b9a954e2.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 16ad2021..044e38f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.20.6 (2024-05-14) + +Full Changelog: [v0.20.5...v0.20.6](https://github.com/Finch-API/finch-api-python/compare/v0.20.5...v0.20.6) + +### Bug Fixes + +* **api:** correct authentication methods type ([#381](https://github.com/Finch-API/finch-api-python/issues/381)) ([bcf80af](https://github.com/Finch-API/finch-api-python/commit/bcf80af518349e89c56f99ce70392e8c4ec8d6f0)) + + +### Chores + +* **internal:** bump pydantic dependency ([#379](https://github.com/Finch-API/finch-api-python/issues/379)) ([fa53eb7](https://github.com/Finch-API/finch-api-python/commit/fa53eb76a06a88d4b007116c7794c0693438f784)) +* **internal:** minor updates ([#377](https://github.com/Finch-API/finch-api-python/issues/377)) ([f2f1236](https://github.com/Finch-API/finch-api-python/commit/f2f12364794b7da31f2cb80b395d36a80fa03ca3)) +* **tests:** update client ID example value ([#382](https://github.com/Finch-API/finch-api-python/issues/382)) ([56b9ec4](https://github.com/Finch-API/finch-api-python/commit/56b9ec415fed15123503ce1819d996d13813bb36)) + ## 0.20.5 (2024-05-10) Full Changelog: [v0.20.4...v0.20.5](https://github.com/Finch-API/finch-api-python/compare/v0.20.4...v0.20.5) diff --git a/pyproject.toml b/pyproject.toml index d887f0df..82ae0faf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "finch-api" -version = "0.20.5" +version = "0.20.6" description = "The official Python library for the Finch API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/requirements-dev.lock b/requirements-dev.lock index 0407fa1e..9087219f 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -59,9 +59,9 @@ pluggy==1.3.0 # via pytest py==1.11.0 # via pytest -pydantic==2.4.2 +pydantic==2.7.1 # via finch-api -pydantic-core==2.10.1 +pydantic-core==2.18.2 # via pydantic pyright==1.1.359 pytest==7.1.1 diff --git a/requirements.lock b/requirements.lock index 94daba33..ca7f06dd 100644 --- a/requirements.lock +++ b/requirements.lock @@ -29,9 +29,9 @@ httpx==0.25.2 idna==3.4 # via anyio # via httpx -pydantic==2.4.2 +pydantic==2.7.1 # via finch-api -pydantic-core==2.10.1 +pydantic-core==2.18.2 # via pydantic sniffio==1.3.0 # via anyio diff --git a/src/finch/_models.py b/src/finch/_models.py index ff3f54e2..75c68cc7 100644 --- a/src/finch/_models.py +++ b/src/finch/_models.py @@ -62,7 +62,7 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: - from pydantic_core.core_schema import ModelField, ModelFieldsSchema + from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema __all__ = ["BaseModel", "GenericModel"] @@ -251,7 +251,9 @@ def model_dump( exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, - warnings: bool = True, + warnings: bool | Literal["none", "warn", "error"] = True, + context: dict[str, Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -279,6 +281,10 @@ def model_dump( raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") return super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, @@ -300,7 +306,9 @@ def model_dump_json( exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, - warnings: bool = True, + warnings: bool | Literal["none", "warn", "error"] = True, + context: dict[str, Any] | None = None, + serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -324,6 +332,10 @@ def model_dump_json( raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, @@ -550,7 +562,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, field_schema = field["schema"] if field_schema["type"] == "literal": - for entry in field_schema["expected"]: + for entry in cast("LiteralSchema", field_schema)["expected"]: if isinstance(entry, str): mapping[entry] = variant else: diff --git a/src/finch/_version.py b/src/finch/_version.py index d4373eb4..5d1dfa81 100644 --- a/src/finch/_version.py +++ b/src/finch/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "finch" -__version__ = "0.20.5" # x-release-please-version +__version__ = "0.20.6" # x-release-please-version diff --git a/src/finch/types/hris/employment_data.py b/src/finch/types/hris/employment_data.py index 57a19763..6c9a161d 100644 --- a/src/finch/types/hris/employment_data.py +++ b/src/finch/types/hris/employment_data.py @@ -78,6 +78,8 @@ class EmploymentData(BaseModel): last_name: Optional[str] = None """The legal last name of the individual.""" + latest_rehire_date: Optional[str] = None + location: Optional[Location] = None manager: Optional[Manager] = None diff --git a/src/finch/types/introspection.py b/src/finch/types/introspection.py index b5c34216..888dd7c4 100644 --- a/src/finch/types/introspection.py +++ b/src/finch/types/introspection.py @@ -6,17 +6,17 @@ from .._models import BaseModel from .shared.connection_status_type import ConnectionStatusType -__all__ = ["Introspection", "AuthenticationMethods", "AuthenticationMethodsConnectionStatus"] +__all__ = ["Introspection", "AuthenticationMethod", "AuthenticationMethodConnectionStatus"] -class AuthenticationMethodsConnectionStatus(BaseModel): +class AuthenticationMethodConnectionStatus(BaseModel): message: Optional[str] = None status: Optional[ConnectionStatusType] = None -class AuthenticationMethods(BaseModel): - connection_status: Optional[AuthenticationMethodsConnectionStatus] = None +class AuthenticationMethod(BaseModel): + connection_status: Optional[AuthenticationMethodConnectionStatus] = None type: Optional[str] = None @@ -25,7 +25,7 @@ class Introspection(BaseModel): account_id: str """The Finch uuid of the account used to connect this company.""" - authentication_methods: AuthenticationMethods + authentication_methods: List[AuthenticationMethod] client_id: str """The client id of the application associated with the `access_token`.""" diff --git a/src/finch/types/provider.py b/src/finch/types/provider.py index 58c62d3a..9967cd34 100644 --- a/src/finch/types/provider.py +++ b/src/finch/types/provider.py @@ -413,6 +413,9 @@ class Provider(BaseModel): authentication_methods: Optional[List[AuthenticationMethod]] = None """The list of authentication methods supported by the provider.""" + beta: Optional[bool] = None + """`true` if the integration is in a beta state, `false` otherwise""" + display_name: Optional[str] = None """The display name of the payroll provider.""" diff --git a/tests/api_resources/test_access_tokens.py b/tests/api_resources/test_access_tokens.py index 3dcbe2e3..92c16f0e 100644 --- a/tests/api_resources/test_access_tokens.py +++ b/tests/api_resources/test_access_tokens.py @@ -28,7 +28,7 @@ def test_method_create(self, client: Finch) -> None: def test_method_create_with_all_params(self, client: Finch) -> None: access_token = client.access_tokens.create( code="", - client_id="", + client_id="12345678-1234-1234-1234-123456789ABC", client_secret="", redirect_uri="https://example.com", ) @@ -73,7 +73,7 @@ async def test_method_create(self, async_client: AsyncFinch) -> None: async def test_method_create_with_all_params(self, async_client: AsyncFinch) -> None: access_token = await async_client.access_tokens.create( code="", - client_id="", + client_id="12345678-1234-1234-1234-123456789ABC", client_secret="", redirect_uri="https://example.com", ) diff --git a/tests/conftest.py b/tests/conftest.py index 0a070000..6ba51ce7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,7 +27,7 @@ def event_loop() -> Iterator[asyncio.AbstractEventLoop]: base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") access_token = "My Access Token" -client_id = "My Client ID" +client_id = "4ab15e51-11ad-49f4-acae-f343b7794375" client_secret = "My Client Secret" diff --git a/tests/test_client.py b/tests/test_client.py index 6585e9e3..7fb8e3d8 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -26,7 +26,7 @@ base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") access_token = "My Access Token" -client_id = "My Client ID" +client_id = "4ab15e51-11ad-49f4-acae-f343b7794375" client_secret = "My Client Secret" @@ -73,9 +73,9 @@ def test_copy(self) -> None: assert copied.access_token == "another My Access Token" assert self.client.access_token == "My Access Token" - copied = self.client.copy(client_id="another My Client ID") - assert copied.client_id == "another My Client ID" - assert self.client.client_id == "My Client ID" + copied = self.client.copy(client_id="another 4ab15e51-11ad-49f4-acae-f343b7794375") + assert copied.client_id == "another 4ab15e51-11ad-49f4-acae-f343b7794375" + assert self.client.client_id == "4ab15e51-11ad-49f4-acae-f343b7794375" copied = self.client.copy(client_secret="another My Client Secret") assert copied.client_secret == "another My Client Secret" @@ -989,9 +989,9 @@ def test_copy(self) -> None: assert copied.access_token == "another My Access Token" assert self.client.access_token == "My Access Token" - copied = self.client.copy(client_id="another My Client ID") - assert copied.client_id == "another My Client ID" - assert self.client.client_id == "My Client ID" + copied = self.client.copy(client_id="another 4ab15e51-11ad-49f4-acae-f343b7794375") + assert copied.client_id == "another 4ab15e51-11ad-49f4-acae-f343b7794375" + assert self.client.client_id == "4ab15e51-11ad-49f4-acae-f343b7794375" copied = self.client.copy(client_secret="another My Client Secret") assert copied.client_secret == "another My Client Secret" diff --git a/tests/test_models.py b/tests/test_models.py index acf48894..786b717d 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -31,7 +31,7 @@ class NestedModel(BaseModel): # mismatched types m = NestedModel.construct(nested="hello!") - assert m.nested == "hello!" + assert cast(Any, m.nested) == "hello!" def test_optional_nested_model() -> None: @@ -48,7 +48,7 @@ class NestedModel(BaseModel): # mismatched types m3 = NestedModel.construct(nested={"foo"}) assert isinstance(cast(Any, m3.nested), set) - assert m3.nested == {"foo"} + assert cast(Any, m3.nested) == {"foo"} def test_list_nested_model() -> None: @@ -323,7 +323,7 @@ class Model(BaseModel): assert len(m.items) == 2 assert isinstance(m.items[0], Submodel1) assert m.items[0].level == -1 - assert m.items[1] == 156 + assert cast(Any, m.items[1]) == 156 def test_union_of_lists() -> None: @@ -355,7 +355,7 @@ class Model(BaseModel): assert len(m.items) == 2 assert isinstance(m.items[0], SubModel1) assert m.items[0].level == -1 - assert m.items[1] == 156 + assert cast(Any, m.items[1]) == 156 def test_dict_of_union() -> None: diff --git a/tests/test_transform.py b/tests/test_transform.py index 187d119e..d8becec4 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -260,20 +260,22 @@ class MyModel(BaseModel): @parametrize @pytest.mark.asyncio async def test_pydantic_model_to_dictionary(use_async: bool) -> None: - assert await transform(MyModel(foo="hi!"), Any, use_async) == {"foo": "hi!"} - assert await transform(MyModel.construct(foo="hi!"), Any, use_async) == {"foo": "hi!"} + assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"} @parametrize @pytest.mark.asyncio async def test_pydantic_empty_model(use_async: bool) -> None: - assert await transform(MyModel.construct(), Any, use_async) == {} + assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {} @parametrize @pytest.mark.asyncio async def test_pydantic_unknown_field(use_async: bool) -> None: - assert await transform(MyModel.construct(my_untyped_field=True), Any, use_async) == {"my_untyped_field": True} + assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == { + "my_untyped_field": True + } @parametrize @@ -285,7 +287,7 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None: params = await transform(model, Any, use_async) else: params = await transform(model, Any, use_async) - assert params == {"foo": True} + assert cast(Any, params) == {"foo": True} @parametrize @@ -297,7 +299,7 @@ async def test_pydantic_mismatched_object_type(use_async: bool) -> None: params = await transform(model, Any, use_async) else: params = await transform(model, Any, use_async) - assert params == {"foo": {"hello": "world"}} + assert cast(Any, params) == {"foo": {"hello": "world"}} class ModelNestedObjects(BaseModel): @@ -309,7 +311,7 @@ class ModelNestedObjects(BaseModel): async def test_pydantic_nested_objects(use_async: bool) -> None: model = ModelNestedObjects.construct(nested={"foo": "stainless"}) assert isinstance(model.nested, MyModel) - assert await transform(model, Any, use_async) == {"nested": {"foo": "stainless"}} + assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}} class ModelWithDefaultField(BaseModel): @@ -325,19 +327,19 @@ async def test_pydantic_default_field(use_async: bool) -> None: model = ModelWithDefaultField.construct() assert model.with_none_default is None assert model.with_str_default == "foo" - assert await transform(model, Any, use_async) == {} + assert cast(Any, await transform(model, Any, use_async)) == {} # should be included when the default value is explicitly given model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") assert model.with_none_default is None assert model.with_str_default == "foo" - assert await transform(model, Any, use_async) == {"with_none_default": None, "with_str_default": "foo"} + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"} # should be included when a non-default value is explicitly given model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") assert model.with_none_default == "bar" assert model.with_str_default == "baz" - assert await transform(model, Any, use_async) == {"with_none_default": "bar", "with_str_default": "baz"} + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} class TypedDictIterableUnion(TypedDict):