From a05398c6e06119d43cbe124cb2ecf92db24dbe3f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 3 Oct 2023 00:23:47 -0400 Subject: [PATCH] feat(api): add `/forward` endpoint and other updates (#115) --- .stats.yml | 2 +- api.md | 23 +- src/finch/_client.py | 141 ++++++ src/finch/resources/__init__.py | 14 +- src/finch/resources/employer/__init__.py | 6 + src/finch/resources/employer/benefits.py | 110 +++++ src/finch/resources/employer/employer.py | 29 ++ src/finch/types/__init__.py | 3 + src/finch/types/benefit_support_type.py | 170 +++++++ src/finch/types/employer/__init__.py | 8 + .../types/employer/benefit_register_params.py | 19 + .../register_company_benefits_response.py | 11 + src/finch/types/forward_response.py | 66 +++ src/finch/types/hris/benefit_frequency.py | 2 +- src/finch/types/hris/employment_data.py | 17 +- src/finch/types/hris/individual.py | 14 + src/finch/types/hris/pay_statement.py | 2 +- src/finch/types/provider.py | 431 +++++++++++++++++- src/finch/types/top_level_forward_params.py | 42 ++ tests/api_resources/employer/__init__.py | 1 + tests/api_resources/employer/test_benefits.py | 54 +++ tests/api_resources/test_top_level.py | 40 ++ 22 files changed, 1196 insertions(+), 9 deletions(-) create mode 100644 src/finch/resources/employer/__init__.py create mode 100644 src/finch/resources/employer/benefits.py create mode 100644 src/finch/resources/employer/employer.py create mode 100644 src/finch/types/benefit_support_type.py create mode 100644 src/finch/types/employer/__init__.py create mode 100644 src/finch/types/employer/benefit_register_params.py create mode 100644 src/finch/types/employer/register_company_benefits_response.py create mode 100644 src/finch/types/forward_response.py create mode 100644 src/finch/types/top_level_forward_params.py create mode 100644 tests/api_resources/employer/__init__.py create mode 100644 tests/api_resources/employer/test_benefits.py diff --git a/.stats.yml b/.stats.yml index 2d2fedb9..6ec2b331 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 18 +configured_endpoints: 20 diff --git a/api.md b/api.md index 24fa1691..d1b897e1 100644 --- a/api.md +++ b/api.md @@ -1,7 +1,14 @@ # Finch +Types: + +```python +from finch.types import ForwardResponse +``` + Methods: +- client.forward(\*\*params) -> ForwardResponse - client.get_access_token(\*args) -> str - client.get_auth_url(\*args) -> str @@ -138,7 +145,7 @@ Methods: Types: ```python -from finch.types import Provider +from finch.types import BenefitSupportType, Provider ``` Methods: @@ -164,3 +171,17 @@ Methods: - client.webhooks.unwrap(\*args) -> object - client.webhooks.verify_signature(\*args) -> None + +# Employer + +## Benefits + +Types: + +```python +from finch.types.employer import RegisterCompanyBenefitsResponse +``` + +Methods: + +- client.employer.benefits.register(\*\*params) -> RegisterCompanyBenefitsResponse diff --git a/src/finch/_client.py b/src/finch/_client.py index 17d3e583..2fd38c5f 100644 --- a/src/finch/_client.py +++ b/src/finch/_client.py @@ -10,9 +10,12 @@ from . import resources, _exceptions from ._qs import Querystring +from .types import ForwardResponse, top_level_forward_params from ._types import ( NOT_GIVEN, + Body, Omit, + Query, Headers, Timeout, NotGiven, @@ -20,6 +23,7 @@ ProxiesTypes, RequestOptions, ) +from ._utils import maybe_transform from ._version import __version__ from ._streaming import Stream as Stream from ._streaming import AsyncStream as AsyncStream @@ -30,6 +34,7 @@ DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient, + make_request_options, ) __all__ = [ @@ -50,6 +55,7 @@ class Finch(SyncAPIClient): providers: resources.Providers account: resources.Account webhooks: resources.Webhooks + employer: resources.Employer # client options access_token: str | None @@ -123,6 +129,7 @@ def __init__( self.providers = resources.Providers(self) self.account = resources.Account(self) self.webhooks = resources.Webhooks(self) + self.employer = resources.Employer(self) @property def qs(self) -> Querystring: @@ -216,6 +223,72 @@ def copy( def __del__(self) -> None: self.close() + def forward( + self, + *, + method: str, + route: str, + data: Optional[str] | NotGiven = NOT_GIVEN, + headers: Optional[object] | NotGiven = NOT_GIVEN, + params: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ForwardResponse: + """The Forward API allows you to make direct requests to an employment system. + + If + Finch’s unified API doesn’t have a data model that cleanly fits your needs, then + Forward allows you to push or pull data models directly against an integration’s + API. + + Args: + method: The HTTP method for the forwarded request. Valid values include: `GET` , `POST` + , `PUT` , `DELETE` , and `PATCH`. + + route: The URL route path for the forwarded request. This value must begin with a + forward-slash ( / ) and may only contain alphanumeric characters, hyphens, and + underscores. + + data: The body for the forwarded request. This value must be specified as either a + string or a valid JSON object. + + headers: The HTTP headers to include on the forwarded request. This value must be + specified as an object of key-value pairs. Example: + `{"Content-Type": "application/xml", "X-API-Version": "v1" }` + + params: The query parameters for the forwarded request. This value must be specified as + a valid JSON object rather than a query string. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self.post( + "/forward", + body=maybe_transform( + { + "method": method, + "route": route, + "data": data, + "headers": headers, + "params": params, + }, + top_level_forward_params.TopLevelForwardParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ForwardResponse, + ) + def get_access_token( self, code: str, @@ -313,6 +386,7 @@ class AsyncFinch(AsyncAPIClient): providers: resources.AsyncProviders account: resources.AsyncAccount webhooks: resources.AsyncWebhooks + employer: resources.AsyncEmployer # client options access_token: str | None @@ -386,6 +460,7 @@ def __init__( self.providers = resources.AsyncProviders(self) self.account = resources.AsyncAccount(self) self.webhooks = resources.AsyncWebhooks(self) + self.employer = resources.AsyncEmployer(self) @property def qs(self) -> Querystring: @@ -482,6 +557,72 @@ def __del__(self) -> None: except Exception: pass + async def forward( + self, + *, + method: str, + route: str, + data: Optional[str] | NotGiven = NOT_GIVEN, + headers: Optional[object] | NotGiven = NOT_GIVEN, + params: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ForwardResponse: + """The Forward API allows you to make direct requests to an employment system. + + If + Finch’s unified API doesn’t have a data model that cleanly fits your needs, then + Forward allows you to push or pull data models directly against an integration’s + API. + + Args: + method: The HTTP method for the forwarded request. Valid values include: `GET` , `POST` + , `PUT` , `DELETE` , and `PATCH`. + + route: The URL route path for the forwarded request. This value must begin with a + forward-slash ( / ) and may only contain alphanumeric characters, hyphens, and + underscores. + + data: The body for the forwarded request. This value must be specified as either a + string or a valid JSON object. + + headers: The HTTP headers to include on the forwarded request. This value must be + specified as an object of key-value pairs. Example: + `{"Content-Type": "application/xml", "X-API-Version": "v1" }` + + params: The query parameters for the forwarded request. This value must be specified as + a valid JSON object rather than a query string. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self.post( + "/forward", + body=maybe_transform( + { + "method": method, + "route": route, + "data": data, + "headers": headers, + "params": params, + }, + top_level_forward_params.TopLevelForwardParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ForwardResponse, + ) + async def get_access_token( self, code: str, diff --git a/src/finch/resources/__init__.py b/src/finch/resources/__init__.py index 1a47fea5..17d2aa49 100644 --- a/src/finch/resources/__init__.py +++ b/src/finch/resources/__init__.py @@ -2,7 +2,19 @@ from .hris import HRIS, AsyncHRIS from .account import Account, AsyncAccount +from .employer import Employer, AsyncEmployer from .webhooks import Webhooks, AsyncWebhooks from .providers import Providers, AsyncProviders -__all__ = ["HRIS", "AsyncHRIS", "Providers", "AsyncProviders", "Account", "AsyncAccount", "Webhooks", "AsyncWebhooks"] +__all__ = [ + "HRIS", + "AsyncHRIS", + "Providers", + "AsyncProviders", + "Account", + "AsyncAccount", + "Webhooks", + "AsyncWebhooks", + "Employer", + "AsyncEmployer", +] diff --git a/src/finch/resources/employer/__init__.py b/src/finch/resources/employer/__init__.py new file mode 100644 index 00000000..bc5b5a4a --- /dev/null +++ b/src/finch/resources/employer/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. + +from .benefits import Benefits, AsyncBenefits +from .employer import Employer, AsyncEmployer + +__all__ = ["Benefits", "AsyncBenefits", "Employer", "AsyncEmployer"] diff --git a/src/finch/resources/employer/benefits.py b/src/finch/resources/employer/benefits.py new file mode 100644 index 00000000..e60e045d --- /dev/null +++ b/src/finch/resources/employer/benefits.py @@ -0,0 +1,110 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform +from ..._resource import SyncAPIResource, AsyncAPIResource +from ...types.hris import BenefitType, BenefitFrequency +from ..._base_client import make_request_options +from ...types.employer import RegisterCompanyBenefitsResponse, benefit_register_params + +__all__ = ["Benefits", "AsyncBenefits"] + + +class Benefits(SyncAPIResource): + def register( + self, + *, + description: str | NotGiven = NOT_GIVEN, + frequency: Optional[BenefitFrequency] | NotGiven = NOT_GIVEN, + type: Optional[BenefitType] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> RegisterCompanyBenefitsResponse: + """ + **Availability: Assisted Benefits providers only** + + Register existing benefits from the customer on the provider, on Finch's end. + Please use the `/provider` endpoint to view available types for each provider. + + Args: + type: Type of benefit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/employer/benefits/register", + body=maybe_transform( + { + "description": description, + "frequency": frequency, + "type": type, + }, + benefit_register_params.BenefitRegisterParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RegisterCompanyBenefitsResponse, + ) + + +class AsyncBenefits(AsyncAPIResource): + async def register( + self, + *, + description: str | NotGiven = NOT_GIVEN, + frequency: Optional[BenefitFrequency] | NotGiven = NOT_GIVEN, + type: Optional[BenefitType] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> RegisterCompanyBenefitsResponse: + """ + **Availability: Assisted Benefits providers only** + + Register existing benefits from the customer on the provider, on Finch's end. + Please use the `/provider` endpoint to view available types for each provider. + + Args: + type: Type of benefit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/employer/benefits/register", + body=maybe_transform( + { + "description": description, + "frequency": frequency, + "type": type, + }, + benefit_register_params.BenefitRegisterParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RegisterCompanyBenefitsResponse, + ) diff --git a/src/finch/resources/employer/employer.py b/src/finch/resources/employer/employer.py new file mode 100644 index 00000000..e03eddf1 --- /dev/null +++ b/src/finch/resources/employer/employer.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .benefits import Benefits, AsyncBenefits +from ..._resource import SyncAPIResource, AsyncAPIResource + +if TYPE_CHECKING: + from ..._client import Finch, AsyncFinch + +__all__ = ["Employer", "AsyncEmployer"] + + +class Employer(SyncAPIResource): + benefits: Benefits + + def __init__(self, client: Finch) -> None: + super().__init__(client) + self.benefits = Benefits(client) + + +class AsyncEmployer(AsyncAPIResource): + benefits: AsyncBenefits + + def __init__(self, client: AsyncFinch) -> None: + super().__init__(client) + self.benefits = AsyncBenefits(client) diff --git a/src/finch/types/__init__.py b/src/finch/types/__init__.py index dc60674b..dcc1fef5 100644 --- a/src/finch/types/__init__.py +++ b/src/finch/types/__init__.py @@ -8,4 +8,7 @@ from .location import Location as Location from .provider import Provider as Provider from .introspection import Introspection as Introspection +from .forward_response import ForwardResponse as ForwardResponse from .disconnect_response import DisconnectResponse as DisconnectResponse +from .benefit_support_type import BenefitSupportType as BenefitSupportType +from .top_level_forward_params import TopLevelForwardParams as TopLevelForwardParams diff --git a/src/finch/types/benefit_support_type.py b/src/finch/types/benefit_support_type.py new file mode 100644 index 00000000..6db34dc9 --- /dev/null +++ b/src/finch/types/benefit_support_type.py @@ -0,0 +1,170 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional +from typing_extensions import Literal + +from .hris import BenefitFrequency +from .._models import BaseModel + +__all__ = [ + "BenefitSupportType", + "SupportedFeatures", + "SupportedOperations", + "SupportedOperationsCompanyBenefits", + "SupportedOperationsIndividualBenefits", +] + + +class SupportedFeatures(BaseModel): + annual_maximum: Optional[bool] = None + """Whether the provider supports an annual maximum for this benefit.""" + + catch_up: Optional[bool] = None + """Whether the provider supports catch up for this benefit. + + This field will only be true for retirement benefits. + """ + + company_contribution: Optional[List[Literal["fixed", "percent"]]] = None + """Supported contribution types. + + An empty array indicates contributions are not supported. + """ + + description: Optional[str] = None + + employee_deduction: Optional[List[Literal["fixed", "percent"]]] = None + """Supported deduction types. + + An empty array indicates deductions are not supported. + """ + + frequencies: Optional[List[Optional[BenefitFrequency]]] = None + """The list of frequencies supported by the provider for this benefit""" + + hsa_contribution_limit: Optional[List[Literal["individual", "family"]]] = None + """Whether the provider supports HSA contribution limits. + + Empty if this feature is not supported for the benefit. This array only has + values for HSA benefits. + """ + + +class SupportedOperationsCompanyBenefits(BaseModel): + create: Optional[ + Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] + ] = None + """ + - `supported`: This operation is supported by both the provider and Finch
+ - `not_supported_by_finch`: This operation is not supported by Finch but + supported by the provider
+ - `not_supported_by_provider`: This operation is not supported by the provider, + so Finch cannot support
+ - `client_access_only`: This behavior is supported by the provider, but only + available to the client and not to Finch + """ + + delete: Optional[ + Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] + ] = None + """ + - `supported`: This operation is supported by both the provider and Finch
+ - `not_supported_by_finch`: This operation is not supported by Finch but + supported by the provider
+ - `not_supported_by_provider`: This operation is not supported by the provider, + so Finch cannot support
+ - `client_access_only`: This behavior is supported by the provider, but only + available to the client and not to Finch + """ + + read: Optional[ + Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] + ] = None + """ + - `supported`: This operation is supported by both the provider and Finch
+ - `not_supported_by_finch`: This operation is not supported by Finch but + supported by the provider
+ - `not_supported_by_provider`: This operation is not supported by the provider, + so Finch cannot support
+ - `client_access_only`: This behavior is supported by the provider, but only + available to the client and not to Finch + """ + + update: Optional[ + Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] + ] = None + """ + - `supported`: This operation is supported by both the provider and Finch
+ - `not_supported_by_finch`: This operation is not supported by Finch but + supported by the provider
+ - `not_supported_by_provider`: This operation is not supported by the provider, + so Finch cannot support
+ - `client_access_only`: This behavior is supported by the provider, but only + available to the client and not to Finch + """ + + +class SupportedOperationsIndividualBenefits(BaseModel): + create: Optional[ + Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] + ] = None + """ + - `supported`: This operation is supported by both the provider and Finch
+ - `not_supported_by_finch`: This operation is not supported by Finch but + supported by the provider
+ - `not_supported_by_provider`: This operation is not supported by the provider, + so Finch cannot support
+ - `client_access_only`: This behavior is supported by the provider, but only + available to the client and not to Finch + """ + + delete: Optional[ + Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] + ] = None + """ + - `supported`: This operation is supported by both the provider and Finch
+ - `not_supported_by_finch`: This operation is not supported by Finch but + supported by the provider
+ - `not_supported_by_provider`: This operation is not supported by the provider, + so Finch cannot support
+ - `client_access_only`: This behavior is supported by the provider, but only + available to the client and not to Finch + """ + + read: Optional[ + Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] + ] = None + """ + - `supported`: This operation is supported by both the provider and Finch
+ - `not_supported_by_finch`: This operation is not supported by Finch but + supported by the provider
+ - `not_supported_by_provider`: This operation is not supported by the provider, + so Finch cannot support
+ - `client_access_only`: This behavior is supported by the provider, but only + available to the client and not to Finch + """ + + update: Optional[ + Literal["supported", "not_supported_by_finch", "not_supported_by_provider", "client_access_only"] + ] = None + """ + - `supported`: This operation is supported by both the provider and Finch
+ - `not_supported_by_finch`: This operation is not supported by Finch but + supported by the provider
+ - `not_supported_by_provider`: This operation is not supported by the provider, + so Finch cannot support
+ - `client_access_only`: This behavior is supported by the provider, but only + available to the client and not to Finch + """ + + +class SupportedOperations(BaseModel): + company_benefits: Optional[SupportedOperationsCompanyBenefits] = None + + individual_benefits: Optional[SupportedOperationsIndividualBenefits] = None + + +class BenefitSupportType(BaseModel): + supported_features: Optional[SupportedFeatures] = None + + supported_operations: Optional[SupportedOperations] = None diff --git a/src/finch/types/employer/__init__.py b/src/finch/types/employer/__init__.py new file mode 100644 index 00000000..60887cf5 --- /dev/null +++ b/src/finch/types/employer/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .benefit_register_params import BenefitRegisterParams as BenefitRegisterParams +from .register_company_benefits_response import ( + RegisterCompanyBenefitsResponse as RegisterCompanyBenefitsResponse, +) diff --git a/src/finch/types/employer/benefit_register_params.py b/src/finch/types/employer/benefit_register_params.py new file mode 100644 index 00000000..cd916ad6 --- /dev/null +++ b/src/finch/types/employer/benefit_register_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from ..hris import BenefitType, BenefitFrequency + +__all__ = ["BenefitRegisterParams"] + + +class BenefitRegisterParams(TypedDict, total=False): + description: str + + frequency: Optional[BenefitFrequency] + + type: Optional[BenefitType] + """Type of benefit.""" diff --git a/src/finch/types/employer/register_company_benefits_response.py b/src/finch/types/employer/register_company_benefits_response.py new file mode 100644 index 00000000..dd64e3d7 --- /dev/null +++ b/src/finch/types/employer/register_company_benefits_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. + +from ..._models import BaseModel + +__all__ = ["RegisterCompanyBenefitsResponse"] + + +class RegisterCompanyBenefitsResponse(BaseModel): + benefit_id: str + + job_id: str diff --git a/src/finch/types/forward_response.py b/src/finch/types/forward_response.py new file mode 100644 index 00000000..2e33fbfc --- /dev/null +++ b/src/finch/types/forward_response.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ForwardResponse", "Request"] + + +class Request(BaseModel): + data: Optional[str] + """The body that was specified for the forwarded request. + + If a value was not specified in the original request, this value will be + returned as null ; otherwise, this value will always be returned as a string. + """ + + headers: Optional[object] + """The specified HTTP headers that were included in the forwarded request. + + If no headers were specified, this will be returned as `null`. + """ + + method: str + """The HTTP method that was specified for the forwarded request. + + Valid values include: `GET` , `POST` , `PUT` , `DELETE` , and `PATCH`. + """ + + params: Optional[object] + """The query parameters that were included in the forwarded request. + + If no query parameters were specified, this will be returned as `null`. + """ + + route: str + """The URL route path that was specified for the forwarded request.""" + + +class ForwardResponse(BaseModel): + data: Optional[str] + """ + A string representation of the HTTP response body of the forwarded request’s + response received from the underlying integration’s API. This field may be null + in the case where the upstream system’s response is empty. + """ + + headers: Optional[object] + """ + The HTTP headers of the forwarded request’s response, exactly as received from + the underlying integration’s API. + """ + + request: Request + """ + An object containing details of your original forwarded request, for your ease + of reference. + """ + + status_code: int = FieldInfo(alias="statusCode") + """ + The HTTP status code of the forwarded request’s response, exactly received from + the underlying integration’s API. This value will be returned as an integer. + """ diff --git a/src/finch/types/hris/benefit_frequency.py b/src/finch/types/hris/benefit_frequency.py index b4a7414f..515774fe 100644 --- a/src/finch/types/hris/benefit_frequency.py +++ b/src/finch/types/hris/benefit_frequency.py @@ -5,4 +5,4 @@ __all__ = ["BenefitFrequency"] -BenefitFrequency = Optional[Literal["one_time", "every_paycheck"]] +BenefitFrequency = Optional[Literal["one_time", "every_paycheck", "monthly"]] diff --git a/src/finch/types/hris/employment_data.py b/src/finch/types/hris/employment_data.py index 4ae7d44a..618fe692 100644 --- a/src/finch/types/hris/employment_data.py +++ b/src/finch/types/hris/employment_data.py @@ -9,7 +9,13 @@ from ..._models import BaseModel from ..location import Location -__all__ = ["EmploymentData", "Department", "Employment", "Manager"] +__all__ = ["EmploymentData", "CustomField", "Department", "Employment", "Manager"] + + +class CustomField(BaseModel): + name: Optional[str] = None + + value: Optional[object] = None class Department(BaseModel): @@ -41,6 +47,12 @@ class EmploymentData(BaseModel): class_code: Optional[str] = None """Worker's compensation classification code for this employee""" + custom_fields: Optional[List[CustomField]] = None + """Custom fields for the individual. + + These are fields which are defined by the employer in the system. + """ + department: Optional[Department] = None """The department object.""" @@ -82,6 +94,9 @@ class EmploymentData(BaseModel): Please reach out to your Finch representative if you would like access. """ + source_id: Optional[str] = None + """The source system's unique employment identifier for this individual""" + start_date: Optional[str] = None title: Optional[str] = None diff --git a/src/finch/types/hris/individual.py b/src/finch/types/hris/individual.py index 89e3e5bf..8f8ae112 100644 --- a/src/finch/types/hris/individual.py +++ b/src/finch/types/hris/individual.py @@ -29,6 +29,20 @@ class Individual(BaseModel): emails: Optional[List[Email]] = None + ethnicity: Optional[ + Literal[ + "asian", + "white", + "black_or_african_american", + "native_hawaiian_or_pacific_islander", + "american_indian_or_alaska_native", + "hispanic_or_latino", + "two_or_more_races", + "decline_to_specify", + ] + ] = None + """The EEOC-defined ethnicity of the individual.""" + first_name: Optional[str] = None """The legal first name of the individual.""" diff --git a/src/finch/types/hris/pay_statement.py b/src/finch/types/hris/pay_statement.py index f9d5515e..3d25a99c 100644 --- a/src/finch/types/hris/pay_statement.py +++ b/src/finch/types/hris/pay_statement.py @@ -117,7 +117,7 @@ class PayStatement(BaseModel): taxes: Optional[List[Optional[Tax]]] = None """The array of taxes objects associated with this pay statement.""" - total_hours: Optional[int] = None + total_hours: Optional[float] = None """The number of hours worked for this pay period""" type: Optional[Literal["regular_payroll", "off_cycle_payroll", "one_time_payment"]] = None diff --git a/src/finch/types/provider.py b/src/finch/types/provider.py index 880077a0..73b08fc3 100644 --- a/src/finch/types/provider.py +++ b/src/finch/types/provider.py @@ -1,16 +1,440 @@ # File generated from our OpenAPI spec by Stainless. from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo from .._models import BaseModel +from .benefit_support_type import BenefitSupportType + +__all__ = [ + "Provider", + "AuthenticationMethod", + "AuthenticationMethodBenefitsSupport", + "AuthenticationMethodSupportedFields", + "AuthenticationMethodSupportedFieldsCompany", + "AuthenticationMethodSupportedFieldsCompanyAccounts", + "AuthenticationMethodSupportedFieldsCompanyDepartments", + "AuthenticationMethodSupportedFieldsCompanyDepartmentsParent", + "AuthenticationMethodSupportedFieldsCompanyEntity", + "AuthenticationMethodSupportedFieldsCompanyLocations", + "AuthenticationMethodSupportedFieldsDirectory", + "AuthenticationMethodSupportedFieldsDirectoryIndividuals", + "AuthenticationMethodSupportedFieldsDirectoryIndividualsManager", + "AuthenticationMethodSupportedFieldsDirectoryPaging", + "AuthenticationMethodSupportedFieldsEmployment", + "AuthenticationMethodSupportedFieldsEmploymentDepartment", + "AuthenticationMethodSupportedFieldsEmploymentEmployment", + "AuthenticationMethodSupportedFieldsEmploymentIncome", + "AuthenticationMethodSupportedFieldsEmploymentLocation", + "AuthenticationMethodSupportedFieldsIndividual", + "AuthenticationMethodSupportedFieldsIndividualEmails", + "AuthenticationMethodSupportedFieldsIndividualPhoneNumbers", + "AuthenticationMethodSupportedFieldsIndividualResidence", + "AuthenticationMethodSupportedFieldsPayStatement", + "AuthenticationMethodSupportedFieldsPayStatementPaging", + "AuthenticationMethodSupportedFieldsPayStatementPayStatements", + "AuthenticationMethodSupportedFieldsPayStatementPayStatementsEarnings", + "AuthenticationMethodSupportedFieldsPayStatementPayStatementsEmployeeDeductions", + "AuthenticationMethodSupportedFieldsPayStatementPayStatementsEmployerDeductions", + "AuthenticationMethodSupportedFieldsPayStatementPayStatementsTaxes", + "AuthenticationMethodSupportedFieldsPayment", + "AuthenticationMethodSupportedFieldsPaymentPayPeriod", +] + + +class AuthenticationMethodBenefitsSupport(BaseModel): + retirement_401k: Optional[BenefitSupportType] = FieldInfo(alias="401k", default=None) + + retirement_401k_loan: Optional[BenefitSupportType] = FieldInfo(alias="401k_loan", default=None) + + retirement_401k_roth: Optional[BenefitSupportType] = FieldInfo(alias="401k_roth", default=None) + + retirement_403b: Optional[BenefitSupportType] = FieldInfo(alias="403b", default=None) + + retirement_403b_roth: Optional[BenefitSupportType] = FieldInfo(alias="403b_roth", default=None) + + retirement_457: Optional[BenefitSupportType] = FieldInfo(alias="457", default=None) + + retirement_457_roth: Optional[BenefitSupportType] = FieldInfo(alias="457_roth", default=None) + + commuter: Optional[BenefitSupportType] = None + + custom_post_tax: Optional[BenefitSupportType] = None + + custom_pre_tax: Optional[BenefitSupportType] = None + + fsa_dependent_care: Optional[BenefitSupportType] = None + + fsa_medical: Optional[BenefitSupportType] = None + + hsa_post: Optional[BenefitSupportType] = None + + hsa_pre: Optional[BenefitSupportType] = None + + s125_dental: Optional[BenefitSupportType] = None + + s125_medical: Optional[BenefitSupportType] = None + + s125_vision: Optional[BenefitSupportType] = None + + simple: Optional[BenefitSupportType] = None + + simple_ira: Optional[BenefitSupportType] = None + + +class AuthenticationMethodSupportedFieldsCompanyAccounts(BaseModel): + account_name: Optional[bool] = None + + account_number: Optional[bool] = None + + account_type: Optional[bool] = None + + institution_name: Optional[bool] = None + + routing_number: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsCompanyDepartmentsParent(BaseModel): + name: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsCompanyDepartments(BaseModel): + name: Optional[bool] = None + + parent: Optional[AuthenticationMethodSupportedFieldsCompanyDepartmentsParent] = None + + +class AuthenticationMethodSupportedFieldsCompanyEntity(BaseModel): + subtype: Optional[bool] = None + + type: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsCompanyLocations(BaseModel): + city: Optional[bool] = None + + country: Optional[bool] = None + + line1: Optional[bool] = None + + line2: Optional[bool] = None + + postal_code: Optional[bool] = None + + state: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsCompany(BaseModel): + id: Optional[bool] = None + + accounts: Optional[AuthenticationMethodSupportedFieldsCompanyAccounts] = None + + departments: Optional[AuthenticationMethodSupportedFieldsCompanyDepartments] = None + + ein: Optional[bool] = None + + entity: Optional[AuthenticationMethodSupportedFieldsCompanyEntity] = None + + legal_name: Optional[bool] = None + + locations: Optional[AuthenticationMethodSupportedFieldsCompanyLocations] = None + + primary_email: Optional[bool] = None + + primary_phone_number: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsDirectoryIndividualsManager(BaseModel): + id: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsDirectoryIndividuals(BaseModel): + id: Optional[bool] = None + + department: Optional[object] = None + + first_name: Optional[bool] = None + + is_active: Optional[bool] = None + + last_name: Optional[bool] = None + + manager: Optional[AuthenticationMethodSupportedFieldsDirectoryIndividualsManager] = None + + middle_name: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsDirectoryPaging(BaseModel): + count: Optional[bool] = None + + offset: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsDirectory(BaseModel): + individuals: Optional[AuthenticationMethodSupportedFieldsDirectoryIndividuals] = None + + paging: Optional[AuthenticationMethodSupportedFieldsDirectoryPaging] = None + + +class AuthenticationMethodSupportedFieldsEmploymentDepartment(BaseModel): + name: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsEmploymentEmployment(BaseModel): + subtype: Optional[bool] = None + + type: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsEmploymentIncome(BaseModel): + amount: Optional[bool] = None + + currency: Optional[bool] = None + + unit: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsEmploymentLocation(BaseModel): + city: Optional[bool] = None + + country: Optional[bool] = None + + line1: Optional[bool] = None + + line2: Optional[bool] = None + + postal_code: Optional[bool] = None + + state: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsEmployment(BaseModel): + id: Optional[bool] = None + + class_code: Optional[bool] = None + + custom_fields: Optional[bool] = None + + department: Optional[AuthenticationMethodSupportedFieldsEmploymentDepartment] = None + + employment: Optional[AuthenticationMethodSupportedFieldsEmploymentEmployment] = None + + end_date: Optional[bool] = None + + first_name: Optional[bool] = None + + income_history: Optional[bool] = None + + income: Optional[AuthenticationMethodSupportedFieldsEmploymentIncome] = None + + is_active: Optional[bool] = None + + last_name: Optional[bool] = None -__all__ = ["Provider"] + location: Optional[AuthenticationMethodSupportedFieldsEmploymentLocation] = None + + manager: Optional[object] = None + + middle_name: Optional[bool] = None + + start_date: Optional[bool] = None + + title: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsIndividualEmails(BaseModel): + data: Optional[bool] = None + + type: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsIndividualPhoneNumbers(BaseModel): + data: Optional[bool] = None + + type: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsIndividualResidence(BaseModel): + city: Optional[bool] = None + + country: Optional[bool] = None + + line1: Optional[bool] = None + + line2: Optional[bool] = None + + postal_code: Optional[bool] = None + + state: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsIndividual(BaseModel): + id: Optional[bool] = None + + dob: Optional[bool] = None + + emails: Optional[AuthenticationMethodSupportedFieldsIndividualEmails] = None + + ethnicity: Optional[bool] = None + + first_name: Optional[bool] = None + + gender: Optional[bool] = None + + last_name: Optional[bool] = None + + middle_name: Optional[bool] = None + + phone_numbers: Optional[AuthenticationMethodSupportedFieldsIndividualPhoneNumbers] = None + + preferred_name: Optional[bool] = None + + residence: Optional[AuthenticationMethodSupportedFieldsIndividualResidence] = None + + ssn: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsPayStatementPaging(BaseModel): + count: bool + + offset: bool + + +class AuthenticationMethodSupportedFieldsPayStatementPayStatementsEarnings(BaseModel): + amount: Optional[bool] = None + + currency: Optional[bool] = None + + name: Optional[bool] = None + + type: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsPayStatementPayStatementsEmployeeDeductions(BaseModel): + amount: Optional[bool] = None + + currency: Optional[bool] = None + + name: Optional[bool] = None + + pre_tax: Optional[bool] = None + + type: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsPayStatementPayStatementsEmployerDeductions(BaseModel): + amount: Optional[bool] = None + + currency: Optional[bool] = None + + name: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsPayStatementPayStatementsTaxes(BaseModel): + amount: Optional[bool] = None + + currency: Optional[bool] = None + + employer: Optional[bool] = None + + name: Optional[bool] = None + + type: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsPayStatementPayStatements(BaseModel): + earnings: Optional[AuthenticationMethodSupportedFieldsPayStatementPayStatementsEarnings] = None + + employee_deductions: Optional[AuthenticationMethodSupportedFieldsPayStatementPayStatementsEmployeeDeductions] = None + + employer_deductions: Optional[AuthenticationMethodSupportedFieldsPayStatementPayStatementsEmployerDeductions] = None + + gross_pay: Optional[bool] = None + + individual_id: Optional[bool] = None + + net_pay: Optional[bool] = None + + payment_method: Optional[bool] = None + + taxes: Optional[AuthenticationMethodSupportedFieldsPayStatementPayStatementsTaxes] = None + + total_hours: Optional[bool] = None + + type: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsPayStatement(BaseModel): + paging: Optional[AuthenticationMethodSupportedFieldsPayStatementPaging] = None + + pay_statements: Optional[AuthenticationMethodSupportedFieldsPayStatementPayStatements] = None + + +class AuthenticationMethodSupportedFieldsPaymentPayPeriod(BaseModel): + end_date: Optional[bool] = None + + start_date: Optional[bool] = None + + +class AuthenticationMethodSupportedFieldsPayment(BaseModel): + id: Optional[bool] = None + + company_debit: Optional[bool] = None + + debit_date: Optional[bool] = None + + employee_taxes: Optional[bool] = None + + employer_taxes: Optional[bool] = None + + gross_pay: Optional[bool] = None + + individual_ids: Optional[bool] = None + + net_pay: Optional[bool] = None + + pay_date: Optional[bool] = None + + pay_period: Optional[AuthenticationMethodSupportedFieldsPaymentPayPeriod] = None + + +class AuthenticationMethodSupportedFields(BaseModel): + company: Optional[AuthenticationMethodSupportedFieldsCompany] = None + + directory: Optional[AuthenticationMethodSupportedFieldsDirectory] = None + + employment: Optional[AuthenticationMethodSupportedFieldsEmployment] = None + + individual: Optional[AuthenticationMethodSupportedFieldsIndividual] = None + + pay_statement: Optional[AuthenticationMethodSupportedFieldsPayStatement] = None + + payment: Optional[AuthenticationMethodSupportedFieldsPayment] = None + + +class AuthenticationMethod(BaseModel): + benefits_support: Optional[AuthenticationMethodBenefitsSupport] = None + """Each benefit type and their supported features. + + If the benefit type is not supported, the property will be null + """ + + supported_fields: Optional[AuthenticationMethodSupportedFields] = None + """The supported data fields returned by our HR and payroll endpoints""" + + type: Optional[Literal["assisted", "credential", "api_token", "oauth"]] = None + """The type of authentication method.""" class Provider(BaseModel): id: Optional[str] = None """The id of the payroll provider used in Connect.""" + authentication_methods: Optional[List[AuthenticationMethod]] = None + """The list of authentication methods supported by the provider.""" + display_name: Optional[str] = None """The display name of the payroll provider.""" @@ -22,8 +446,9 @@ class Provider(BaseModel): manual: Optional[bool] = None """ - Whether the Finch integration with this provider uses the Assisted Connect Flow - by default. + [DEPRECATED] Whether the Finch integration with this provider uses the Assisted + Connect Flow by default. This field is now deprecated. Please check for a `type` + of `assisted` in the `authentication_methods` field instead. """ mfa_required: Optional[bool] = None diff --git a/src/finch/types/top_level_forward_params.py b/src/finch/types/top_level_forward_params.py new file mode 100644 index 00000000..1bd9a58f --- /dev/null +++ b/src/finch/types/top_level_forward_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["TopLevelForwardParams"] + + +class TopLevelForwardParams(TypedDict, total=False): + method: Required[str] + """The HTTP method for the forwarded request. + + Valid values include: `GET` , `POST` , `PUT` , `DELETE` , and `PATCH`. + """ + + route: Required[str] + """The URL route path for the forwarded request. + + This value must begin with a forward-slash ( / ) and may only contain + alphanumeric characters, hyphens, and underscores. + """ + + data: Optional[str] + """The body for the forwarded request. + + This value must be specified as either a string or a valid JSON object. + """ + + headers: Optional[object] + """The HTTP headers to include on the forwarded request. + + This value must be specified as an object of key-value pairs. Example: + `{"Content-Type": "application/xml", "X-API-Version": "v1" }` + """ + + params: Optional[object] + """The query parameters for the forwarded request. + + This value must be specified as a valid JSON object rather than a query string. + """ diff --git a/tests/api_resources/employer/__init__.py b/tests/api_resources/employer/__init__.py new file mode 100644 index 00000000..1016754e --- /dev/null +++ b/tests/api_resources/employer/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/employer/test_benefits.py b/tests/api_resources/employer/test_benefits.py new file mode 100644 index 00000000..8d733f3e --- /dev/null +++ b/tests/api_resources/employer/test_benefits.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from finch import Finch, AsyncFinch +from tests.utils import assert_matches_type +from finch.types.employer import RegisterCompanyBenefitsResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") +access_token = os.environ.get("API_KEY", "something1234") + + +class TestBenefits: + strict_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + loose_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_register(self, client: Finch) -> None: + benefit = client.employer.benefits.register() + assert_matches_type(RegisterCompanyBenefitsResponse, benefit, path=["response"]) + + @parametrize + def test_method_register_with_all_params(self, client: Finch) -> None: + benefit = client.employer.benefits.register( + description="string", + frequency="one_time", + type="401k", + ) + assert_matches_type(RegisterCompanyBenefitsResponse, benefit, path=["response"]) + + +class TestAsyncBenefits: + strict_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) + loose_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_register(self, client: AsyncFinch) -> None: + benefit = await client.employer.benefits.register() + assert_matches_type(RegisterCompanyBenefitsResponse, benefit, path=["response"]) + + @parametrize + async def test_method_register_with_all_params(self, client: AsyncFinch) -> None: + benefit = await client.employer.benefits.register( + description="string", + frequency="one_time", + type="401k", + ) + assert_matches_type(RegisterCompanyBenefitsResponse, benefit, path=["response"]) diff --git a/tests/api_resources/test_top_level.py b/tests/api_resources/test_top_level.py index daf3c42c..92717ab1 100644 --- a/tests/api_resources/test_top_level.py +++ b/tests/api_resources/test_top_level.py @@ -7,6 +7,8 @@ import pytest from finch import Finch, AsyncFinch +from finch.types import ForwardResponse +from tests.utils import assert_matches_type base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") access_token = os.environ.get("API_KEY", "something1234") @@ -17,8 +19,46 @@ class TestTopLevel: loose_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + @parametrize + def test_method_forward(self, client: Finch) -> None: + top_level = client.forward( + method="string", + route="string", + ) + assert_matches_type(ForwardResponse, top_level, path=["response"]) + + @parametrize + def test_method_forward_with_all_params(self, client: Finch) -> None: + top_level = client.forward( + method="string", + route="string", + data="string", + headers={}, + params={}, + ) + assert_matches_type(ForwardResponse, top_level, path=["response"]) + class TestAsyncTopLevel: strict_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) loose_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_forward(self, client: AsyncFinch) -> None: + top_level = await client.forward( + method="string", + route="string", + ) + assert_matches_type(ForwardResponse, top_level, path=["response"]) + + @parametrize + async def test_method_forward_with_all_params(self, client: AsyncFinch) -> None: + top_level = await client.forward( + method="string", + route="string", + data="string", + headers={}, + params={}, + ) + assert_matches_type(ForwardResponse, top_level, path=["response"])