From eb6c135106a0e11e15aa6245db72a019ea90668e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:23:21 +0000 Subject: [PATCH 1/8] chore(internal): fix compat model_dump method when warnings are passed (#633) --- src/lithic/_compat.py | 3 ++- tests/test_models.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lithic/_compat.py b/src/lithic/_compat.py index 4794129c..df173f85 100644 --- a/src/lithic/_compat.py +++ b/src/lithic/_compat.py @@ -145,7 +145,8 @@ def model_dump( exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, - warnings=warnings, + # warnings are not supported in Pydantic v1 + warnings=warnings if PYDANTIC_V2 else True, ) return cast( "dict[str, Any]", diff --git a/tests/test_models.py b/tests/test_models.py index 620ea7e3..790991b5 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -561,6 +561,14 @@ class Model(BaseModel): m.model_dump(warnings=False) +def test_compat_method_no_error_for_warnings() -> None: + class Model(BaseModel): + foo: Optional[str] + + m = Model(foo="hello") + assert isinstance(model_dump(m, warnings=False), dict) + + def test_to_json() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) From aed46f67456c19cf389275607f593e8e269e3237 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:08:09 +0000 Subject: [PATCH 2/8] docs: add info log level to readme (#635) --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 32b45dbf..f7588143 100644 --- a/README.md +++ b/README.md @@ -295,12 +295,14 @@ client = Lithic( We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. -You can enable logging by setting the environment variable `LITHIC_LOG` to `debug`. +You can enable logging by setting the environment variable `LITHIC_LOG` to `info`. ```shell -$ export LITHIC_LOG=debug +$ export LITHIC_LOG=info ``` +Or to `debug` for more verbose logging. + ### How to tell whether `None` means `null` or missing In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: From 78321efbac2e6da20afb852d6bf38fa371751d99 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:24:33 +0000 Subject: [PATCH 3/8] chore: remove now unused `cached-property` dep (#636) --- pyproject.toml | 1 - src/lithic/_compat.py | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0957c843..ed9ddfff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,6 @@ dependencies = [ "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", - "cached-property; python_version < '3.8'", ] requires-python = ">= 3.8" classifiers = [ diff --git a/src/lithic/_compat.py b/src/lithic/_compat.py index df173f85..92d9ee61 100644 --- a/src/lithic/_compat.py +++ b/src/lithic/_compat.py @@ -214,9 +214,6 @@ def __set_name__(self, owner: type[Any], name: str) -> None: ... # __set__ is not defined at runtime, but @cached_property is designed to be settable def __set__(self, instance: object, value: _T) -> None: ... else: - try: - from functools import cached_property as cached_property - except ImportError: - from cached_property import cached_property as cached_property + from functools import cached_property as cached_property typed_cached_property = cached_property From 6436634a1a0ccc9b6ec070c2553bdb45ab656dd6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 21:20:46 +0000 Subject: [PATCH 4/8] feat(api): updates to Auth Rules numeric types, new Card Types and Authorization Rule Backtests (#637) - Specifies numeric types on Auth Rules to be integers - adds `replacement_for` field to Card create requests - improvements to documentation - adds Authorization Rules Backtests --- src/lithic/resources/cards/cards.py | 12 +- .../resources/transactions/transactions.py | 192 +++++++++--------- .../types/auth_rules/v2_apply_response.py | 5 +- .../types/auth_rules/v2_create_params.py | 6 +- .../types/auth_rules/v2_create_response.py | 5 +- .../types/auth_rules/v2_draft_params.py | 2 +- .../types/auth_rules/v2_draft_response.py | 5 +- .../types/auth_rules/v2_list_response.py | 5 +- .../types/auth_rules/v2_promote_response.py | 5 +- .../types/auth_rules/v2_retrieve_response.py | 5 +- .../types/auth_rules/v2_update_response.py | 5 +- src/lithic/types/card.py | 12 +- src/lithic/types/card_create_params.py | 6 +- .../types/shared/velocity_limit_params.py | 6 +- .../shared_params/velocity_limit_params.py | 6 +- .../authentication_retrieve_response.py | 6 +- src/lithic/types/transaction.py | 33 +-- ...on_simulate_authorization_advice_params.py | 2 +- ...ansaction_simulate_authorization_params.py | 12 +- .../transaction_simulate_clearing_params.py | 14 +- .../types/transaction_simulate_void_params.py | 3 +- 21 files changed, 196 insertions(+), 151 deletions(-) diff --git a/src/lithic/resources/cards/cards.py b/src/lithic/resources/cards/cards.py index 69f559f0..1139d633 100644 --- a/src/lithic/resources/cards/cards.py +++ b/src/lithic/resources/cards/cards.py @@ -113,7 +113,7 @@ def with_streaming_response(self) -> CardsWithStreamingResponse: def create( self, *, - type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL"], + type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"], account_token: str | NotGiven = NOT_GIVEN, card_program_token: str | NotGiven = NOT_GIVEN, carrier: Carrier | NotGiven = NOT_GIVEN, @@ -157,6 +157,10 @@ def create( - `SINGLE_USE` - Card is closed upon first successful authorization. - `MERCHANT_LOCKED` - _[Deprecated]_ Card is locked to the first merchant that successfully authorizes the card. + - `UNLOCKED` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please use + VIRTUAL instead. + - `DIGITAL_WALLET` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please + use VIRTUAL instead. account_token: Globally unique identifier for the account that the card will be associated with. Required for programs enrolling users using the @@ -1006,7 +1010,7 @@ def with_streaming_response(self) -> AsyncCardsWithStreamingResponse: async def create( self, *, - type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL"], + type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"], account_token: str | NotGiven = NOT_GIVEN, card_program_token: str | NotGiven = NOT_GIVEN, carrier: Carrier | NotGiven = NOT_GIVEN, @@ -1050,6 +1054,10 @@ async def create( - `SINGLE_USE` - Card is closed upon first successful authorization. - `MERCHANT_LOCKED` - _[Deprecated]_ Card is locked to the first merchant that successfully authorizes the card. + - `UNLOCKED` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please use + VIRTUAL instead. + - `DIGITAL_WALLET` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please + use VIRTUAL instead. account_token: Globally unique identifier for the account that the card will be associated with. Required for programs enrolling users using the diff --git a/src/lithic/resources/transactions/transactions.py b/src/lithic/resources/transactions/transactions.py index 6e94ec8a..eebb2a7c 100644 --- a/src/lithic/resources/transactions/transactions.py +++ b/src/lithic/resources/transactions/transactions.py @@ -226,19 +226,20 @@ def simulate_authorization( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateAuthorizationResponse: """ - Simulates an authorization request from the payment network as if it came from a - merchant acquirer. If you're configured for ASA, simulating auths requires your - ASA client to be set up properly (respond with a valid JSON to the ASA request). - For users that are not configured for ASA, a daily transaction limit of $5000 - USD is applied by default. This limit can be modified via the + Simulates an authorization request from the card network as if it came from a + merchant acquirer. If you are configured for ASA, simulating authorizations + requires your ASA client to be set up properly, i.e. be able to respond to the + ASA request with a valid JSON. For users that are not configured for ASA, a + daily transaction limit of $5000 USD is applied by default. You can update this + limit via the [update account](https://docs.lithic.com/reference/patchaccountbytoken) endpoint. Args: amount: Amount (in cents) to authorize. For credit authorizations and financial credit authorizations, any value entered will be converted into a negative amount in - the simulated transaction. For example, entering 100 in this field will appear - as a -100 amount in the transaction. For balance inquiries, this field must be + the simulated transaction. For example, entering 100 in this field will result + in a -100 amount in the transaction. For balance inquiries, this field must be set to 0. descriptor: Merchant descriptor. @@ -265,12 +266,12 @@ def simulate_authorization( - `AUTHORIZATION` is a dual message purchase authorization, meaning a subsequent clearing step is required to settle the transaction. - - `BALANCE_INQUIRY` is a $0 authorization that includes a request for the - balance held on the card, and is most typically seen when a cardholder - requests to view a card's balance at an ATM. + - `BALANCE_INQUIRY` is a $0 authorization requesting the balance held on the + card, and is most often observed when a cardholder requests to view a card's + balance at an ATM. - `CREDIT_AUTHORIZATION` is a dual message request from a merchant to authorize - a refund or credit, meaning a subsequent clearing step is required to settle - the transaction. + a refund, meaning a subsequent clearing step is required to settle the + transaction. - `FINANCIAL_AUTHORIZATION` is a single message request from a merchant to debit funds immediately (such as an ATM withdrawal), and no subsequent clearing is required to settle the transaction. @@ -321,12 +322,12 @@ def simulate_authorization_advice( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateAuthorizationAdviceResponse: """ - Simulates an authorization advice request from the payment network as if it came - from a merchant acquirer. An authorization advice request changes the amount of - the transaction. + Simulates an authorization advice from the card network as if it came from a + merchant acquirer. An authorization advice changes the pending amount of the + transaction. Args: - token: The transaction token returned from the /v1/simulate/authorize response. + token: The transaction token returned from the /v1/simulate/authorize. response. amount: Amount (in cents) to authorize. This amount will override the transaction's amount that was originally set by /v1/simulate/authorize. @@ -366,24 +367,27 @@ def simulate_clearing( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateClearingResponse: - """Clears an existing authorization. + """Clears an existing authorization, either debit or credit. - After this event, the transaction is no longer - pending. + After this event, the + transaction transitions from `PENDING` to `SETTLED` status. - If no `amount` is supplied to this endpoint, the amount of the transaction will - be captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. Args: token: The transaction token returned from the /v1/simulate/authorize response. - amount: Amount (in cents) to complete. Typically this will match the original - authorization, but may be more or less. + amount: Amount (in cents) to clear. Typically this will match the amount in the original + authorization, but can be higher or lower. The sign of this amount will + automatically match the sign of the original authorization's amount. For + example, entering 100 in this field will result in a -100 amount in the + transaction, if the original authorization is a credit authorization. - If no amount is supplied to this endpoint, the amount of the transaction will be - captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. extra_headers: Send extra headers @@ -423,11 +427,10 @@ def simulate_credit_authorization( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateCreditAuthorizationResponse: - """Simulates a credit authorization advice message from the payment network. + """Simulates a credit authorization advice from the card network. - This - message indicates that a credit authorization was approved on your behalf by the - network. + This message + indicates that the network approved a credit authorization on your behalf. Args: amount: Amount (in cents). Any value entered will be converted into a negative amount in @@ -483,10 +486,11 @@ def simulate_return( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateReturnResponse: - """Returns (aka refunds) an amount back to a card. + """Returns, or refunds, an amount back to a card. - Returns are cleared immediately - and do not spend time in a `PENDING` state. + Returns simulated via this + endpoint clear immediately, without prior authorization, and result in a + `SETTLED` transaction status. Args: amount: Amount (in cents) to authorize. @@ -530,10 +534,11 @@ def simulate_return_reversal( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateReturnReversalResponse: - """ - Voids a settled credit transaction – i.e., a transaction with a negative amount - and `SETTLED` status. These can be credit authorizations that have already - cleared or financial credit authorizations. + """Reverses a return, i.e. + + a credit transaction with a `SETTLED` status. Returns + can be financial credit authorizations, or credit authorizations that have + cleared. Args: token: The transaction token returned from the /v1/simulate/authorize response. @@ -570,19 +575,18 @@ def simulate_void( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateVoidResponse: - """Voids an existing, uncleared (aka pending) authorization. + """Voids a pending authorization. - If amount is not sent - the full amount will be voided. Cannot be used on partially completed - transactions, but can be used on partially voided transactions. _Note that - simulating an authorization expiry on credit authorizations or credit - authorization advice is not currently supported but will be added soon._ + If `amount` is not set, the full amount will be + voided. Can be used on partially voided transactions but not partially cleared + transactions. _Simulating an authorization expiry on credit authorizations or + credit authorization advice is not currently supported but will be added soon._ Args: token: The transaction token returned from the /v1/simulate/authorize response. - amount: Amount (in cents) to void. Typically this will match the original authorization, - but may be less. + amount: Amount (in cents) to void. Typically this will match the amount in the original + authorization, but can be less. type: Type of event to simulate. Defaults to `AUTHORIZATION_REVERSAL`. @@ -783,19 +787,20 @@ async def simulate_authorization( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateAuthorizationResponse: """ - Simulates an authorization request from the payment network as if it came from a - merchant acquirer. If you're configured for ASA, simulating auths requires your - ASA client to be set up properly (respond with a valid JSON to the ASA request). - For users that are not configured for ASA, a daily transaction limit of $5000 - USD is applied by default. This limit can be modified via the + Simulates an authorization request from the card network as if it came from a + merchant acquirer. If you are configured for ASA, simulating authorizations + requires your ASA client to be set up properly, i.e. be able to respond to the + ASA request with a valid JSON. For users that are not configured for ASA, a + daily transaction limit of $5000 USD is applied by default. You can update this + limit via the [update account](https://docs.lithic.com/reference/patchaccountbytoken) endpoint. Args: amount: Amount (in cents) to authorize. For credit authorizations and financial credit authorizations, any value entered will be converted into a negative amount in - the simulated transaction. For example, entering 100 in this field will appear - as a -100 amount in the transaction. For balance inquiries, this field must be + the simulated transaction. For example, entering 100 in this field will result + in a -100 amount in the transaction. For balance inquiries, this field must be set to 0. descriptor: Merchant descriptor. @@ -822,12 +827,12 @@ async def simulate_authorization( - `AUTHORIZATION` is a dual message purchase authorization, meaning a subsequent clearing step is required to settle the transaction. - - `BALANCE_INQUIRY` is a $0 authorization that includes a request for the - balance held on the card, and is most typically seen when a cardholder - requests to view a card's balance at an ATM. + - `BALANCE_INQUIRY` is a $0 authorization requesting the balance held on the + card, and is most often observed when a cardholder requests to view a card's + balance at an ATM. - `CREDIT_AUTHORIZATION` is a dual message request from a merchant to authorize - a refund or credit, meaning a subsequent clearing step is required to settle - the transaction. + a refund, meaning a subsequent clearing step is required to settle the + transaction. - `FINANCIAL_AUTHORIZATION` is a single message request from a merchant to debit funds immediately (such as an ATM withdrawal), and no subsequent clearing is required to settle the transaction. @@ -878,12 +883,12 @@ async def simulate_authorization_advice( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateAuthorizationAdviceResponse: """ - Simulates an authorization advice request from the payment network as if it came - from a merchant acquirer. An authorization advice request changes the amount of - the transaction. + Simulates an authorization advice from the card network as if it came from a + merchant acquirer. An authorization advice changes the pending amount of the + transaction. Args: - token: The transaction token returned from the /v1/simulate/authorize response. + token: The transaction token returned from the /v1/simulate/authorize. response. amount: Amount (in cents) to authorize. This amount will override the transaction's amount that was originally set by /v1/simulate/authorize. @@ -923,24 +928,27 @@ async def simulate_clearing( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateClearingResponse: - """Clears an existing authorization. + """Clears an existing authorization, either debit or credit. - After this event, the transaction is no longer - pending. + After this event, the + transaction transitions from `PENDING` to `SETTLED` status. - If no `amount` is supplied to this endpoint, the amount of the transaction will - be captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. Args: token: The transaction token returned from the /v1/simulate/authorize response. - amount: Amount (in cents) to complete. Typically this will match the original - authorization, but may be more or less. + amount: Amount (in cents) to clear. Typically this will match the amount in the original + authorization, but can be higher or lower. The sign of this amount will + automatically match the sign of the original authorization's amount. For + example, entering 100 in this field will result in a -100 amount in the + transaction, if the original authorization is a credit authorization. - If no amount is supplied to this endpoint, the amount of the transaction will be - captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. extra_headers: Send extra headers @@ -980,11 +988,10 @@ async def simulate_credit_authorization( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateCreditAuthorizationResponse: - """Simulates a credit authorization advice message from the payment network. + """Simulates a credit authorization advice from the card network. - This - message indicates that a credit authorization was approved on your behalf by the - network. + This message + indicates that the network approved a credit authorization on your behalf. Args: amount: Amount (in cents). Any value entered will be converted into a negative amount in @@ -1040,10 +1047,11 @@ async def simulate_return( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateReturnResponse: - """Returns (aka refunds) an amount back to a card. + """Returns, or refunds, an amount back to a card. - Returns are cleared immediately - and do not spend time in a `PENDING` state. + Returns simulated via this + endpoint clear immediately, without prior authorization, and result in a + `SETTLED` transaction status. Args: amount: Amount (in cents) to authorize. @@ -1087,10 +1095,11 @@ async def simulate_return_reversal( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateReturnReversalResponse: - """ - Voids a settled credit transaction – i.e., a transaction with a negative amount - and `SETTLED` status. These can be credit authorizations that have already - cleared or financial credit authorizations. + """Reverses a return, i.e. + + a credit transaction with a `SETTLED` status. Returns + can be financial credit authorizations, or credit authorizations that have + cleared. Args: token: The transaction token returned from the /v1/simulate/authorize response. @@ -1127,19 +1136,18 @@ async def simulate_void( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateVoidResponse: - """Voids an existing, uncleared (aka pending) authorization. + """Voids a pending authorization. - If amount is not sent - the full amount will be voided. Cannot be used on partially completed - transactions, but can be used on partially voided transactions. _Note that - simulating an authorization expiry on credit authorizations or credit - authorization advice is not currently supported but will be added soon._ + If `amount` is not set, the full amount will be + voided. Can be used on partially voided transactions but not partially cleared + transactions. _Simulating an authorization expiry on credit authorizations or + credit authorization advice is not currently supported but will be added soon._ Args: token: The transaction token returned from the /v1/simulate/authorize response. - amount: Amount (in cents) to void. Typically this will match the original authorization, - but may be less. + amount: Amount (in cents) to void. Typically this will match the amount in the original + authorization, but can be less. type: Type of event to simulate. Defaults to `AUTHORIZATION_REVERSAL`. diff --git a/src/lithic/types/auth_rules/v2_apply_response.py b/src/lithic/types/auth_rules/v2_apply_response.py index 970dfa0a..4a0aac7a 100644 --- a/src/lithic/types/auth_rules/v2_apply_response.py +++ b/src/lithic/types/auth_rules/v2_apply_response.py @@ -71,7 +71,7 @@ class CurrentVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -145,7 +145,7 @@ class DraftVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -169,6 +169,7 @@ class DraftVersion(BaseModel): class V2ApplyResponse(BaseModel): token: str + """Auth Rule Token""" account_tokens: List[str] """Account tokens to which the Auth Rule applies.""" diff --git a/src/lithic/types/auth_rules/v2_create_params.py b/src/lithic/types/auth_rules/v2_create_params.py index b749e726..a6be31a1 100644 --- a/src/lithic/types/auth_rules/v2_create_params.py +++ b/src/lithic/types/auth_rules/v2_create_params.py @@ -83,7 +83,7 @@ class CreateAuthRuleRequestAccountTokensParametersConditionalBlockParametersCond operation: Literal["IS_ONE_OF", "IS_NOT_ONE_OF", "MATCHES", "DOES_NOT_MATCH", "IS_GREATER_THAN", "IS_LESS_THAN"] """The operation to apply to the attribute""" - value: Union[str, float, List[str]] + value: Union[str, int, List[str]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -155,7 +155,7 @@ class CreateAuthRuleRequestCardTokensParametersConditionalBlockParametersConditi operation: Literal["IS_ONE_OF", "IS_NOT_ONE_OF", "MATCHES", "DOES_NOT_MATCH", "IS_GREATER_THAN", "IS_LESS_THAN"] """The operation to apply to the attribute""" - value: Union[str, float, List[str]] + value: Union[str, int, List[str]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -227,7 +227,7 @@ class CreateAuthRuleRequestProgramLevelParametersConditionalBlockParametersCondi operation: Literal["IS_ONE_OF", "IS_NOT_ONE_OF", "MATCHES", "DOES_NOT_MATCH", "IS_GREATER_THAN", "IS_LESS_THAN"] """The operation to apply to the attribute""" - value: Union[str, float, List[str]] + value: Union[str, int, List[str]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/v2_create_response.py b/src/lithic/types/auth_rules/v2_create_response.py index 6ee72e59..976aeb4d 100644 --- a/src/lithic/types/auth_rules/v2_create_response.py +++ b/src/lithic/types/auth_rules/v2_create_response.py @@ -71,7 +71,7 @@ class CurrentVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -145,7 +145,7 @@ class DraftVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -169,6 +169,7 @@ class DraftVersion(BaseModel): class V2CreateResponse(BaseModel): token: str + """Auth Rule Token""" account_tokens: List[str] """Account tokens to which the Auth Rule applies.""" diff --git a/src/lithic/types/auth_rules/v2_draft_params.py b/src/lithic/types/auth_rules/v2_draft_params.py index a22e7038..382c7f72 100644 --- a/src/lithic/types/auth_rules/v2_draft_params.py +++ b/src/lithic/types/auth_rules/v2_draft_params.py @@ -68,7 +68,7 @@ class ParametersConditionalBlockParametersCondition(TypedDict, total=False): operation: Literal["IS_ONE_OF", "IS_NOT_ONE_OF", "MATCHES", "DOES_NOT_MATCH", "IS_GREATER_THAN", "IS_LESS_THAN"] """The operation to apply to the attribute""" - value: Union[str, float, List[str]] + value: Union[str, int, List[str]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/v2_draft_response.py b/src/lithic/types/auth_rules/v2_draft_response.py index fd8be59a..516b2db7 100644 --- a/src/lithic/types/auth_rules/v2_draft_response.py +++ b/src/lithic/types/auth_rules/v2_draft_response.py @@ -71,7 +71,7 @@ class CurrentVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -145,7 +145,7 @@ class DraftVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -169,6 +169,7 @@ class DraftVersion(BaseModel): class V2DraftResponse(BaseModel): token: str + """Auth Rule Token""" account_tokens: List[str] """Account tokens to which the Auth Rule applies.""" diff --git a/src/lithic/types/auth_rules/v2_list_response.py b/src/lithic/types/auth_rules/v2_list_response.py index 648ce243..b1ac2cc3 100644 --- a/src/lithic/types/auth_rules/v2_list_response.py +++ b/src/lithic/types/auth_rules/v2_list_response.py @@ -71,7 +71,7 @@ class CurrentVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -145,7 +145,7 @@ class DraftVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -169,6 +169,7 @@ class DraftVersion(BaseModel): class V2ListResponse(BaseModel): token: str + """Auth Rule Token""" account_tokens: List[str] """Account tokens to which the Auth Rule applies.""" diff --git a/src/lithic/types/auth_rules/v2_promote_response.py b/src/lithic/types/auth_rules/v2_promote_response.py index 645ebb9d..1ec1e7e3 100644 --- a/src/lithic/types/auth_rules/v2_promote_response.py +++ b/src/lithic/types/auth_rules/v2_promote_response.py @@ -71,7 +71,7 @@ class CurrentVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -145,7 +145,7 @@ class DraftVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -169,6 +169,7 @@ class DraftVersion(BaseModel): class V2PromoteResponse(BaseModel): token: str + """Auth Rule Token""" account_tokens: List[str] """Account tokens to which the Auth Rule applies.""" diff --git a/src/lithic/types/auth_rules/v2_retrieve_response.py b/src/lithic/types/auth_rules/v2_retrieve_response.py index cb834d9b..9f1e3ffb 100644 --- a/src/lithic/types/auth_rules/v2_retrieve_response.py +++ b/src/lithic/types/auth_rules/v2_retrieve_response.py @@ -71,7 +71,7 @@ class CurrentVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -145,7 +145,7 @@ class DraftVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -169,6 +169,7 @@ class DraftVersion(BaseModel): class V2RetrieveResponse(BaseModel): token: str + """Auth Rule Token""" account_tokens: List[str] """Account tokens to which the Auth Rule applies.""" diff --git a/src/lithic/types/auth_rules/v2_update_response.py b/src/lithic/types/auth_rules/v2_update_response.py index fffb3e9f..967ecd99 100644 --- a/src/lithic/types/auth_rules/v2_update_response.py +++ b/src/lithic/types/auth_rules/v2_update_response.py @@ -71,7 +71,7 @@ class CurrentVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -145,7 +145,7 @@ class DraftVersionParametersConditionalBlockParametersCondition(BaseModel): ] = None """The operation to apply to the attribute""" - value: Union[str, float, List[str], None] = None + value: Union[str, int, List[str], None] = None """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" @@ -169,6 +169,7 @@ class DraftVersion(BaseModel): class V2UpdateResponse(BaseModel): token: str + """Auth Rule Token""" account_tokens: List[str] """Account tokens to which the Auth Rule applies.""" diff --git a/src/lithic/types/card.py b/src/lithic/types/card.py index 5ff38920..a31ff96a 100644 --- a/src/lithic/types/card.py +++ b/src/lithic/types/card.py @@ -121,7 +121,7 @@ class Card(BaseModel): manufactured. """ - type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL"] + type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"] """Card types: - `VIRTUAL` - Card will authorize at any merchant and can be added to a digital @@ -134,6 +134,10 @@ class Card(BaseModel): - `SINGLE_USE` - Card is closed upon first successful authorization. - `MERCHANT_LOCKED` - _[Deprecated]_ Card is locked to the first merchant that successfully authorizes the card. + - `UNLOCKED` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please use + VIRTUAL instead. + - `DIGITAL_WALLET` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please + use VIRTUAL instead. """ auth_rule_tokens: Optional[List[str]] = None @@ -191,3 +195,9 @@ class Card(BaseModel): This must be configured with Lithic before use. Specifies the configuration (i.e., physical card art) that the card should be manufactured with. """ + + replacement_for: Optional[str] = None + """ + If the card is a replacement for another card, the globally unique identifier + for the card that was replaced. + """ diff --git a/src/lithic/types/card_create_params.py b/src/lithic/types/card_create_params.py index bcd5d2f5..01c5e9a1 100644 --- a/src/lithic/types/card_create_params.py +++ b/src/lithic/types/card_create_params.py @@ -12,7 +12,7 @@ class CardCreateParams(TypedDict, total=False): - type: Required[Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL"]] + type: Required[Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"]] """Card types: - `VIRTUAL` - Card will authorize at any merchant and can be added to a digital @@ -25,6 +25,10 @@ class CardCreateParams(TypedDict, total=False): - `SINGLE_USE` - Card is closed upon first successful authorization. - `MERCHANT_LOCKED` - _[Deprecated]_ Card is locked to the first merchant that successfully authorizes the card. + - `UNLOCKED` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please use + VIRTUAL instead. + - `DIGITAL_WALLET` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please + use VIRTUAL instead. """ account_token: str diff --git a/src/lithic/types/shared/velocity_limit_params.py b/src/lithic/types/shared/velocity_limit_params.py index 7bac9b17..413a34e2 100644 --- a/src/lithic/types/shared/velocity_limit_params.py +++ b/src/lithic/types/shared/velocity_limit_params.py @@ -28,7 +28,7 @@ class Filters(BaseModel): class VelocityLimitParams(BaseModel): filters: Filters - period: Union[float, VelocityLimitParamsPeriodWindow] + period: Union[int, VelocityLimitParamsPeriodWindow] """The size of the trailing window to calculate Spend Velocity over in seconds. The minimum value is 10 seconds, and the maximum value is 2678400 seconds. @@ -36,14 +36,14 @@ class VelocityLimitParams(BaseModel): scope: Literal["CARD", "ACCOUNT"] - limit_amount: Optional[float] = None + limit_amount: Optional[int] = None """ The maximum amount of spend velocity allowed in the period in minor units (the smallest unit of a currency, e.g. cents for USD). Transactions exceeding this limit will be declined. """ - limit_count: Optional[float] = None + limit_count: Optional[int] = None """ The number of spend velocity impacting transactions may not exceed this limit in the period. Transactions exceeding this limit will be declined. A spend velocity diff --git a/src/lithic/types/shared_params/velocity_limit_params.py b/src/lithic/types/shared_params/velocity_limit_params.py index 531c4f3d..45d56ff3 100644 --- a/src/lithic/types/shared_params/velocity_limit_params.py +++ b/src/lithic/types/shared_params/velocity_limit_params.py @@ -29,7 +29,7 @@ class Filters(TypedDict, total=False): class VelocityLimitParams(TypedDict, total=False): filters: Required[Filters] - period: Required[Union[float, VelocityLimitParamsPeriodWindow]] + period: Required[Union[int, VelocityLimitParamsPeriodWindow]] """The size of the trailing window to calculate Spend Velocity over in seconds. The minimum value is 10 seconds, and the maximum value is 2678400 seconds. @@ -37,14 +37,14 @@ class VelocityLimitParams(TypedDict, total=False): scope: Required[Literal["CARD", "ACCOUNT"]] - limit_amount: Optional[float] + limit_amount: Optional[int] """ The maximum amount of spend velocity allowed in the period in minor units (the smallest unit of a currency, e.g. cents for USD). Transactions exceeding this limit will be declined. """ - limit_count: Optional[float] + limit_count: Optional[int] """ The number of spend velocity impacting transactions may not exceed this limit in the period. Transactions exceeding this limit will be declined. A spend velocity diff --git a/src/lithic/types/three_ds/authentication_retrieve_response.py b/src/lithic/types/three_ds/authentication_retrieve_response.py index 515570dd..3b5a068e 100644 --- a/src/lithic/types/three_ds/authentication_retrieve_response.py +++ b/src/lithic/types/three_ds/authentication_retrieve_response.py @@ -124,14 +124,14 @@ class MerchantRiskIndicator(BaseModel): Maps to EMV 3DS field deliveryTimeframe. """ - gift_card_amount: Optional[float] = None + gift_card_amount: Optional[int] = None """ In prepaid or gift card purchase transactions, purchase amount total in major units (e.g., a purchase of USD $205.10 would be 205). Maps to EMV 3DS field giftCardAmount. """ - gift_card_count: Optional[float] = None + gift_card_count: Optional[int] = None """ In prepaid or gift card purchase transactions, count of individual prepaid or gift cards/codes purchased. Maps to EMV 3DS field giftCardCount. @@ -218,7 +218,7 @@ class AdditionalData(BaseModel): authentication request to be low risk or not. """ - network_risk_score: Optional[float] = None + network_risk_score: Optional[int] = None """ Mastercard only: Assessment by the network of the authentication risk level, with a higher value indicating a higher amount of risk. diff --git a/src/lithic/types/transaction.py b/src/lithic/types/transaction.py index d3dfa409..6f6c3797 100644 --- a/src/lithic/types/transaction.py +++ b/src/lithic/types/transaction.py @@ -33,12 +33,15 @@ class AmountsCardholder(BaseModel): amount: int - """The aggregate settled amount in the cardholder billing currency.""" + """ + The estimated settled amount of the transaction in the cardholder billing + currency. + """ conversion_rate: str """ - The conversion rate used to convert the merchant amount to the cardholder - billing amount. + The exchange rate used to convert the merchant amount to the cardholder billing + amount. """ currency: Currency @@ -51,10 +54,7 @@ class AmountsCardholder(BaseModel): class AmountsHold(BaseModel): amount: int - """ - The aggregate authorization amount of the transaction in the anticipated - settlement currency. - """ + """The pending amount of the transaction in the anticipated settlement currency.""" currency: Currency """ISO 4217 currency. @@ -66,7 +66,7 @@ class AmountsHold(BaseModel): class AmountsMerchant(BaseModel): amount: int - """The aggregate settled amount in the merchant currency.""" + """The settled amount of the transaction in the merchant currency.""" currency: Currency """ISO 4217 currency. @@ -78,7 +78,7 @@ class AmountsMerchant(BaseModel): class AmountsSettlement(BaseModel): amount: int - """The aggregate settled amount in the settlement currency.""" + """The settled amount of the transaction in the settlement currency.""" currency: Currency """ISO 4217 currency. @@ -314,12 +314,12 @@ class TokenInfo(BaseModel): class EventAmountsCardholder(BaseModel): amount: int - """The amount in the cardholder billing currency.""" + """Amount of the event in the cardholder billing currency.""" conversion_rate: str """ - The conversion rate used to convert the merchant amount to the cardholder - billing amount. + Exchange rate used to convert the merchant amount to the cardholder billing + amount. """ currency: Currency @@ -332,7 +332,7 @@ class EventAmountsCardholder(BaseModel): class EventAmountsMerchant(BaseModel): amount: int - """The amount in the merchant currency.""" + """Amount of the event in the merchant currency.""" currency: Currency """ISO 4217 currency. @@ -344,10 +344,13 @@ class EventAmountsMerchant(BaseModel): class EventAmountsSettlement(BaseModel): amount: int - """Amount of the event, if it is financial, in the settlement currency.""" + """Amount of the event, if it is financial, in the settlement currency. + + Non-financial events do not contain this amount because they do not move funds. + """ conversion_rate: str - """Conversion rate used to convert the merchant amount to the settlement amount.""" + """Exchange rate used to convert the merchant amount to the settlement amount.""" currency: Currency """ISO 4217 currency. diff --git a/src/lithic/types/transaction_simulate_authorization_advice_params.py b/src/lithic/types/transaction_simulate_authorization_advice_params.py index 503ec9d5..5e973292 100644 --- a/src/lithic/types/transaction_simulate_authorization_advice_params.py +++ b/src/lithic/types/transaction_simulate_authorization_advice_params.py @@ -9,7 +9,7 @@ class TransactionSimulateAuthorizationAdviceParams(TypedDict, total=False): token: Required[str] - """The transaction token returned from the /v1/simulate/authorize response.""" + """The transaction token returned from the /v1/simulate/authorize. response.""" amount: Required[int] """Amount (in cents) to authorize. diff --git a/src/lithic/types/transaction_simulate_authorization_params.py b/src/lithic/types/transaction_simulate_authorization_params.py index dc48a18c..25cb817d 100644 --- a/src/lithic/types/transaction_simulate_authorization_params.py +++ b/src/lithic/types/transaction_simulate_authorization_params.py @@ -13,7 +13,7 @@ class TransactionSimulateAuthorizationParams(TypedDict, total=False): For credit authorizations and financial credit authorizations, any value entered will be converted into a negative amount in the simulated transaction. For - example, entering 100 in this field will appear as a -100 amount in the + example, entering 100 in this field will result in a -100 amount in the transaction. For balance inquiries, this field must be set to 0. """ @@ -65,12 +65,12 @@ class TransactionSimulateAuthorizationParams(TypedDict, total=False): - `AUTHORIZATION` is a dual message purchase authorization, meaning a subsequent clearing step is required to settle the transaction. - - `BALANCE_INQUIRY` is a $0 authorization that includes a request for the - balance held on the card, and is most typically seen when a cardholder - requests to view a card's balance at an ATM. + - `BALANCE_INQUIRY` is a $0 authorization requesting the balance held on the + card, and is most often observed when a cardholder requests to view a card's + balance at an ATM. - `CREDIT_AUTHORIZATION` is a dual message request from a merchant to authorize - a refund or credit, meaning a subsequent clearing step is required to settle - the transaction. + a refund, meaning a subsequent clearing step is required to settle the + transaction. - `FINANCIAL_AUTHORIZATION` is a single message request from a merchant to debit funds immediately (such as an ATM withdrawal), and no subsequent clearing is required to settle the transaction. diff --git a/src/lithic/types/transaction_simulate_clearing_params.py b/src/lithic/types/transaction_simulate_clearing_params.py index 94475c8e..ab45523f 100644 --- a/src/lithic/types/transaction_simulate_clearing_params.py +++ b/src/lithic/types/transaction_simulate_clearing_params.py @@ -12,11 +12,15 @@ class TransactionSimulateClearingParams(TypedDict, total=False): """The transaction token returned from the /v1/simulate/authorize response.""" amount: int - """Amount (in cents) to complete. + """Amount (in cents) to clear. - Typically this will match the original authorization, but may be more or less. + Typically this will match the amount in the original authorization, but can be + higher or lower. The sign of this amount will automatically match the sign of + the original authorization's amount. For example, entering 100 in this field + will result in a -100 amount in the transaction, if the original authorization + is a credit authorization. - If no amount is supplied to this endpoint, the amount of the transaction will be - captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. """ diff --git a/src/lithic/types/transaction_simulate_void_params.py b/src/lithic/types/transaction_simulate_void_params.py index dc796f1f..e5b10ea9 100644 --- a/src/lithic/types/transaction_simulate_void_params.py +++ b/src/lithic/types/transaction_simulate_void_params.py @@ -14,7 +14,8 @@ class TransactionSimulateVoidParams(TypedDict, total=False): amount: int """Amount (in cents) to void. - Typically this will match the original authorization, but may be less. + Typically this will match the amount in the original authorization, but can be + less. """ type: Literal["AUTHORIZATION_EXPIRY", "AUTHORIZATION_REVERSAL"] From 8125eac6b6bb5e448f8641c362a334f0eab5a5fa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:39:58 +0000 Subject: [PATCH 5/8] chore(api): add backtest methods to AuthRules (#638) --- .stats.yml | 2 +- api.md | 29 +- src/lithic/resources/auth_rules/auth_rules.py | 1 + .../resources/auth_rules/v2/__init__.py | 33 ++ .../resources/auth_rules/v2/backtests.py | 364 ++++++++++++++++++ .../resources/auth_rules/{ => v2}/v2.py | 66 +++- src/lithic/types/auth_rules/v2/__init__.py | 7 + .../auth_rules/v2/backtest_create_params.py | 19 + .../auth_rules/v2/backtest_create_response.py | 12 + .../types/auth_rules/v2/backtest_results.py | 114 ++++++ src/lithic/types/settlement_detail.py | 3 + tests/api_resources/auth_rules/v2/__init__.py | 1 + .../auth_rules/v2/test_backtests.py | 217 +++++++++++ 13 files changed, 842 insertions(+), 26 deletions(-) create mode 100644 src/lithic/resources/auth_rules/v2/__init__.py create mode 100644 src/lithic/resources/auth_rules/v2/backtests.py rename src/lithic/resources/auth_rules/{ => v2}/v2.py (96%) create mode 100644 src/lithic/types/auth_rules/v2/__init__.py create mode 100644 src/lithic/types/auth_rules/v2/backtest_create_params.py create mode 100644 src/lithic/types/auth_rules/v2/backtest_create_response.py create mode 100644 src/lithic/types/auth_rules/v2/backtest_results.py create mode 100644 tests/api_resources/auth_rules/v2/__init__.py create mode 100644 tests/api_resources/auth_rules/v2/test_backtests.py diff --git a/.stats.yml b/.stats.yml index 77de7290..8d78762c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 150 +configured_endpoints: 152 diff --git a/api.md b/api.md index 1305bc6d..9ff4ac64 100644 --- a/api.md +++ b/api.md @@ -94,14 +94,27 @@ from lithic.types.auth_rules import ( Methods: -- client.auth_rules.v2.create(\*\*params) -> V2CreateResponse -- client.auth_rules.v2.retrieve(auth_rule_token) -> V2RetrieveResponse -- client.auth_rules.v2.update(auth_rule_token, \*\*params) -> V2UpdateResponse -- client.auth_rules.v2.list(\*\*params) -> SyncCursorPage[V2ListResponse] -- client.auth_rules.v2.apply(auth_rule_token, \*\*params) -> V2ApplyResponse -- client.auth_rules.v2.draft(auth_rule_token, \*\*params) -> V2DraftResponse -- client.auth_rules.v2.promote(auth_rule_token) -> V2PromoteResponse -- client.auth_rules.v2.report(auth_rule_token) -> V2ReportResponse +- client.auth_rules.v2.create(\*\*params) -> V2CreateResponse +- client.auth_rules.v2.retrieve(auth_rule_token) -> V2RetrieveResponse +- client.auth_rules.v2.update(auth_rule_token, \*\*params) -> V2UpdateResponse +- client.auth_rules.v2.list(\*\*params) -> SyncCursorPage[V2ListResponse] +- client.auth_rules.v2.apply(auth_rule_token, \*\*params) -> V2ApplyResponse +- client.auth_rules.v2.draft(auth_rule_token, \*\*params) -> V2DraftResponse +- client.auth_rules.v2.promote(auth_rule_token) -> V2PromoteResponse +- client.auth_rules.v2.report(auth_rule_token) -> V2ReportResponse + +### Backtests + +Types: + +```python +from lithic.types.auth_rules.v2 import BacktestResults, BacktestCreateResponse +``` + +Methods: + +- client.auth_rules.v2.backtests.create(auth_rule_token, \*\*params) -> BacktestCreateResponse +- client.auth_rules.v2.backtests.retrieve(auth_rule_backtest_token, \*, auth_rule_token) -> BacktestResults # AuthStreamEnrollment diff --git a/src/lithic/resources/auth_rules/auth_rules.py b/src/lithic/resources/auth_rules/auth_rules.py index 387046b5..6ce45ef9 100644 --- a/src/lithic/resources/auth_rules/auth_rules.py +++ b/src/lithic/resources/auth_rules/auth_rules.py @@ -10,6 +10,7 @@ V2WithStreamingResponse, AsyncV2WithStreamingResponse, ) +from .v2.v2 import V2, AsyncV2 from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/lithic/resources/auth_rules/v2/__init__.py b/src/lithic/resources/auth_rules/v2/__init__.py new file mode 100644 index 00000000..aa9d53c0 --- /dev/null +++ b/src/lithic/resources/auth_rules/v2/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .v2 import ( + V2, + AsyncV2, + V2WithRawResponse, + AsyncV2WithRawResponse, + V2WithStreamingResponse, + AsyncV2WithStreamingResponse, +) +from .backtests import ( + Backtests, + AsyncBacktests, + BacktestsWithRawResponse, + AsyncBacktestsWithRawResponse, + BacktestsWithStreamingResponse, + AsyncBacktestsWithStreamingResponse, +) + +__all__ = [ + "Backtests", + "AsyncBacktests", + "BacktestsWithRawResponse", + "AsyncBacktestsWithRawResponse", + "BacktestsWithStreamingResponse", + "AsyncBacktestsWithStreamingResponse", + "V2", + "AsyncV2", + "V2WithRawResponse", + "AsyncV2WithRawResponse", + "V2WithStreamingResponse", + "AsyncV2WithStreamingResponse", +] diff --git a/src/lithic/resources/auth_rules/v2/backtests.py b/src/lithic/resources/auth_rules/v2/backtests.py new file mode 100644 index 00000000..86584912 --- /dev/null +++ b/src/lithic/resources/auth_rules/v2/backtests.py @@ -0,0 +1,364 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.auth_rules.v2 import backtest_create_params +from ....types.auth_rules.v2.backtest_results import BacktestResults +from ....types.auth_rules.v2.backtest_create_response import BacktestCreateResponse + +__all__ = ["Backtests", "AsyncBacktests"] + + +class Backtests(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BacktestsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return BacktestsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BacktestsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return BacktestsWithStreamingResponse(self) + + def create( + self, + auth_rule_token: str, + *, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + start: Union[str, datetime] | 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 | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BacktestCreateResponse: + """ + Initiates a request to asynchronously generate a backtest for an authorization + rule. During backtesting, both the active version (if one exists) and the draft + version of the Authorization Rule are evaluated by replaying historical + transaction data against the rule's conditions. This process allows customers to + simulate and understand the effects of proposed rule changes before deployment. + The generated backtest report provides detailed results showing whether the + draft version of the Auth Rule would have approved or declined historical + transactions which were processed during the backtest period. These reports help + evaluate how changes to rule configurations might affect overall transaction + approval rates. + + The generated backtest report will be delivered asynchronously through a webhook + with `event_type` = `auth_rules.backtest_report.created`. See the docs on + setting up [webhook subscriptions](https://docs.lithic.com/docs/events-api). It + is also possible to request backtest reports on-demand through the + `/v2/auth_rules/{auth_rule_token}/backtests/{auth_rule_backtest_token}` + endpoint. + + Lithic currently supports backtesting for `CONDITIONAL_BLOCK` rules. Backtesting + for `VELOCITY_LIMIT` rules is generally not supported. In specific cases (i.e. + where Lithic has pre-calculated the requested velocity metrics for historical + transactions), a backtest may be feasible. However, such cases are uncommon and + customers should not anticipate support for velocity backtests under most + configurations. If a historical transaction does not feature the required inputs + to evaluate the rule, then it will not be included in the final backtest report. + + Args: + end: The end time of the backtest. + + start: The start time of the backtest. + + 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 + """ + if not auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._post( + f"/v2/auth_rules/{auth_rule_token}/backtests", + body=maybe_transform( + { + "end": end, + "start": start, + }, + backtest_create_params.BacktestCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BacktestCreateResponse, + ) + + def retrieve( + self, + auth_rule_backtest_token: str, + *, + auth_rule_token: str, + # 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 | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BacktestResults: + """ + Returns the backtest results of an authorization rule (if available). + + Backtesting is an asynchronous process that requires time to complete. If a + customer retrieves the backtest results using this endpoint before the report is + fully generated, the response will return null for `results.current_version` and + `results.draft_version`. Customers are advised to wait for the backtest creation + process to complete (as indicated by the webhook event + auth_rules.backtest_report.created) before retrieving results from this + endpoint. + + Backtesting is an asynchronous process, while the backtest is being processed, + results will not be available which will cause `results.current_version` and + `results.draft_version` objects to contain `null`. The entries in `results` will + also always represent the configuration of the rule at the time requests are + made to this endpoint. For example, the results for `current_version` in the + served backtest report will be consistent with which version of the rule is + currently activated in the Auth Stream, regardless of which version of the rule + was active in the Auth Stream at the time a backtest is requested. + + Args: + 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 + """ + if not auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + if not auth_rule_backtest_token: + raise ValueError( + f"Expected a non-empty value for `auth_rule_backtest_token` but received {auth_rule_backtest_token!r}" + ) + return self._get( + f"/v2/auth_rules/{auth_rule_token}/backtests/{auth_rule_backtest_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BacktestResults, + ) + + +class AsyncBacktests(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBacktestsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncBacktestsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBacktestsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncBacktestsWithStreamingResponse(self) + + async def create( + self, + auth_rule_token: str, + *, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + start: Union[str, datetime] | 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 | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BacktestCreateResponse: + """ + Initiates a request to asynchronously generate a backtest for an authorization + rule. During backtesting, both the active version (if one exists) and the draft + version of the Authorization Rule are evaluated by replaying historical + transaction data against the rule's conditions. This process allows customers to + simulate and understand the effects of proposed rule changes before deployment. + The generated backtest report provides detailed results showing whether the + draft version of the Auth Rule would have approved or declined historical + transactions which were processed during the backtest period. These reports help + evaluate how changes to rule configurations might affect overall transaction + approval rates. + + The generated backtest report will be delivered asynchronously through a webhook + with `event_type` = `auth_rules.backtest_report.created`. See the docs on + setting up [webhook subscriptions](https://docs.lithic.com/docs/events-api). It + is also possible to request backtest reports on-demand through the + `/v2/auth_rules/{auth_rule_token}/backtests/{auth_rule_backtest_token}` + endpoint. + + Lithic currently supports backtesting for `CONDITIONAL_BLOCK` rules. Backtesting + for `VELOCITY_LIMIT` rules is generally not supported. In specific cases (i.e. + where Lithic has pre-calculated the requested velocity metrics for historical + transactions), a backtest may be feasible. However, such cases are uncommon and + customers should not anticipate support for velocity backtests under most + configurations. If a historical transaction does not feature the required inputs + to evaluate the rule, then it will not be included in the final backtest report. + + Args: + end: The end time of the backtest. + + start: The start time of the backtest. + + 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 + """ + if not auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._post( + f"/v2/auth_rules/{auth_rule_token}/backtests", + body=await async_maybe_transform( + { + "end": end, + "start": start, + }, + backtest_create_params.BacktestCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BacktestCreateResponse, + ) + + async def retrieve( + self, + auth_rule_backtest_token: str, + *, + auth_rule_token: str, + # 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 | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BacktestResults: + """ + Returns the backtest results of an authorization rule (if available). + + Backtesting is an asynchronous process that requires time to complete. If a + customer retrieves the backtest results using this endpoint before the report is + fully generated, the response will return null for `results.current_version` and + `results.draft_version`. Customers are advised to wait for the backtest creation + process to complete (as indicated by the webhook event + auth_rules.backtest_report.created) before retrieving results from this + endpoint. + + Backtesting is an asynchronous process, while the backtest is being processed, + results will not be available which will cause `results.current_version` and + `results.draft_version` objects to contain `null`. The entries in `results` will + also always represent the configuration of the rule at the time requests are + made to this endpoint. For example, the results for `current_version` in the + served backtest report will be consistent with which version of the rule is + currently activated in the Auth Stream, regardless of which version of the rule + was active in the Auth Stream at the time a backtest is requested. + + Args: + 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 + """ + if not auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + if not auth_rule_backtest_token: + raise ValueError( + f"Expected a non-empty value for `auth_rule_backtest_token` but received {auth_rule_backtest_token!r}" + ) + return await self._get( + f"/v2/auth_rules/{auth_rule_token}/backtests/{auth_rule_backtest_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BacktestResults, + ) + + +class BacktestsWithRawResponse: + def __init__(self, backtests: Backtests) -> None: + self._backtests = backtests + + self.create = _legacy_response.to_raw_response_wrapper( + backtests.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + backtests.retrieve, + ) + + +class AsyncBacktestsWithRawResponse: + def __init__(self, backtests: AsyncBacktests) -> None: + self._backtests = backtests + + self.create = _legacy_response.async_to_raw_response_wrapper( + backtests.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + backtests.retrieve, + ) + + +class BacktestsWithStreamingResponse: + def __init__(self, backtests: Backtests) -> None: + self._backtests = backtests + + self.create = to_streamed_response_wrapper( + backtests.create, + ) + self.retrieve = to_streamed_response_wrapper( + backtests.retrieve, + ) + + +class AsyncBacktestsWithStreamingResponse: + def __init__(self, backtests: AsyncBacktests) -> None: + self._backtests = backtests + + self.create = async_to_streamed_response_wrapper( + backtests.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + backtests.retrieve, + ) diff --git a/src/lithic/resources/auth_rules/v2.py b/src/lithic/resources/auth_rules/v2/v2.py similarity index 96% rename from src/lithic/resources/auth_rules/v2.py rename to src/lithic/resources/auth_rules/v2/v2.py index 72de4301..f0c1fd5e 100644 --- a/src/lithic/resources/auth_rules/v2.py +++ b/src/lithic/resources/auth_rules/v2/v2.py @@ -7,32 +7,44 @@ import httpx -from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( required_args, maybe_transform, async_maybe_transform, ) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import AsyncPaginator, make_request_options -from ...types.auth_rules import v2_list_params, v2_apply_params, v2_draft_params, v2_create_params, v2_update_params -from ...types.auth_rules.v2_list_response import V2ListResponse -from ...types.auth_rules.v2_apply_response import V2ApplyResponse -from ...types.auth_rules.v2_draft_response import V2DraftResponse -from ...types.auth_rules.v2_create_response import V2CreateResponse -from ...types.auth_rules.v2_report_response import V2ReportResponse -from ...types.auth_rules.v2_update_response import V2UpdateResponse -from ...types.auth_rules.v2_promote_response import V2PromoteResponse -from ...types.auth_rules.v2_retrieve_response import V2RetrieveResponse +from .backtests import ( + Backtests, + AsyncBacktests, + BacktestsWithRawResponse, + AsyncBacktestsWithRawResponse, + BacktestsWithStreamingResponse, + AsyncBacktestsWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.auth_rules import v2_list_params, v2_apply_params, v2_draft_params, v2_create_params, v2_update_params +from ....types.auth_rules.v2_list_response import V2ListResponse +from ....types.auth_rules.v2_apply_response import V2ApplyResponse +from ....types.auth_rules.v2_draft_response import V2DraftResponse +from ....types.auth_rules.v2_create_response import V2CreateResponse +from ....types.auth_rules.v2_report_response import V2ReportResponse +from ....types.auth_rules.v2_update_response import V2UpdateResponse +from ....types.auth_rules.v2_promote_response import V2PromoteResponse +from ....types.auth_rules.v2_retrieve_response import V2RetrieveResponse __all__ = ["V2", "AsyncV2"] class V2(SyncAPIResource): + @cached_property + def backtests(self) -> Backtests: + return Backtests(self._client) + @cached_property def with_raw_response(self) -> V2WithRawResponse: """ @@ -615,6 +627,10 @@ def report( class AsyncV2(AsyncAPIResource): + @cached_property + def backtests(self) -> AsyncBacktests: + return AsyncBacktests(self._client) + @cached_property def with_raw_response(self) -> AsyncV2WithRawResponse: """ @@ -1225,6 +1241,10 @@ def __init__(self, v2: V2) -> None: v2.report, ) + @cached_property + def backtests(self) -> BacktestsWithRawResponse: + return BacktestsWithRawResponse(self._v2.backtests) + class AsyncV2WithRawResponse: def __init__(self, v2: AsyncV2) -> None: @@ -1255,6 +1275,10 @@ def __init__(self, v2: AsyncV2) -> None: v2.report, ) + @cached_property + def backtests(self) -> AsyncBacktestsWithRawResponse: + return AsyncBacktestsWithRawResponse(self._v2.backtests) + class V2WithStreamingResponse: def __init__(self, v2: V2) -> None: @@ -1285,6 +1309,10 @@ def __init__(self, v2: V2) -> None: v2.report, ) + @cached_property + def backtests(self) -> BacktestsWithStreamingResponse: + return BacktestsWithStreamingResponse(self._v2.backtests) + class AsyncV2WithStreamingResponse: def __init__(self, v2: AsyncV2) -> None: @@ -1314,3 +1342,7 @@ def __init__(self, v2: AsyncV2) -> None: self.report = async_to_streamed_response_wrapper( v2.report, ) + + @cached_property + def backtests(self) -> AsyncBacktestsWithStreamingResponse: + return AsyncBacktestsWithStreamingResponse(self._v2.backtests) diff --git a/src/lithic/types/auth_rules/v2/__init__.py b/src/lithic/types/auth_rules/v2/__init__.py new file mode 100644 index 00000000..c665d2de --- /dev/null +++ b/src/lithic/types/auth_rules/v2/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .backtest_results import BacktestResults as BacktestResults +from .backtest_create_params import BacktestCreateParams as BacktestCreateParams +from .backtest_create_response import BacktestCreateResponse as BacktestCreateResponse diff --git a/src/lithic/types/auth_rules/v2/backtest_create_params.py b/src/lithic/types/auth_rules/v2/backtest_create_params.py new file mode 100644 index 00000000..fec2e270 --- /dev/null +++ b/src/lithic/types/auth_rules/v2/backtest_create_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["BacktestCreateParams"] + + +class BacktestCreateParams(TypedDict, total=False): + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """The end time of the backtest.""" + + start: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """The start time of the backtest.""" diff --git a/src/lithic/types/auth_rules/v2/backtest_create_response.py b/src/lithic/types/auth_rules/v2/backtest_create_response.py new file mode 100644 index 00000000..28ee75ea --- /dev/null +++ b/src/lithic/types/auth_rules/v2/backtest_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["BacktestCreateResponse"] + + +class BacktestCreateResponse(BaseModel): + backtest_token: Optional[str] = None + """Auth Rule Backtest Token""" diff --git a/src/lithic/types/auth_rules/v2/backtest_results.py b/src/lithic/types/auth_rules/v2/backtest_results.py new file mode 100644 index 00000000..20f6916a --- /dev/null +++ b/src/lithic/types/auth_rules/v2/backtest_results.py @@ -0,0 +1,114 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ...._models import BaseModel + +__all__ = [ + "BacktestResults", + "Results", + "ResultsCurrentVersion", + "ResultsCurrentVersionExample", + "ResultsDraftVersion", + "ResultsDraftVersionExample", + "SimulationParameters", +] + + +class ResultsCurrentVersionExample(BaseModel): + approved: Optional[bool] = None + """Whether the rule would have approved the authorization request.""" + + event_token: Optional[str] = None + """The authorization request event token.""" + + timestamp: Optional[datetime] = None + """The timestamp of the authorization request event.""" + + +class ResultsCurrentVersion(BaseModel): + approved: Optional[int] = None + """ + The total number of historical transactions approved by this rule during the + backtest period, or the number of transactions that would have been approved if + the rule was evaluated in shadow mode. + """ + + declined: Optional[int] = None + """ + The total number of historical transactions declined by this rule during the + backtest period, or the number of transactions that would have been declined if + the rule was evaluated in shadow mode. + """ + + examples: Optional[List[ResultsCurrentVersionExample]] = None + """Example authorization request events that would have been approved or declined.""" + + version: Optional[int] = None + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class ResultsDraftVersionExample(BaseModel): + approved: Optional[bool] = None + """Whether the rule would have approved the authorization request.""" + + event_token: Optional[str] = None + """The authorization request event token.""" + + timestamp: Optional[datetime] = None + """The timestamp of the authorization request event.""" + + +class ResultsDraftVersion(BaseModel): + approved: Optional[int] = None + """ + The total number of historical transactions approved by this rule during the + backtest period, or the number of transactions that would have been approved if + the rule was evaluated in shadow mode. + """ + + declined: Optional[int] = None + """ + The total number of historical transactions declined by this rule during the + backtest period, or the number of transactions that would have been declined if + the rule was evaluated in shadow mode. + """ + + examples: Optional[List[ResultsDraftVersionExample]] = None + """Example authorization request events that would have been approved or declined.""" + + version: Optional[int] = None + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class Results(BaseModel): + current_version: Optional[ResultsCurrentVersion] = None + + draft_version: Optional[ResultsDraftVersion] = None + + +class SimulationParameters(BaseModel): + auth_rule_token: Optional[str] = None + """Auth Rule Token""" + + end: Optional[datetime] = None + """The end time of the simulation.""" + + start: Optional[datetime] = None + """The start time of the simulation.""" + + +class BacktestResults(BaseModel): + backtest_token: str + """Auth Rule Backtest Token""" + + results: Results + + simulation_parameters: SimulationParameters diff --git a/src/lithic/types/settlement_detail.py b/src/lithic/types/settlement_detail.py index 975a87e4..c21d9f5e 100644 --- a/src/lithic/types/settlement_detail.py +++ b/src/lithic/types/settlement_detail.py @@ -102,3 +102,6 @@ class SettlementDetail(BaseModel): updated: datetime """Date and time when the transaction first occurred. UTC time zone.""" + + fee_description: Optional[str] = None + """Network's description of a fee, only present on records with type `FEE`.""" diff --git a/tests/api_resources/auth_rules/v2/__init__.py b/tests/api_resources/auth_rules/v2/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/auth_rules/v2/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/auth_rules/v2/test_backtests.py b/tests/api_resources/auth_rules/v2/test_backtests.py new file mode 100644 index 00000000..12a05cec --- /dev/null +++ b/tests/api_resources/auth_rules/v2/test_backtests.py @@ -0,0 +1,217 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic._utils import parse_datetime +from lithic.types.auth_rules.v2 import BacktestResults, BacktestCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestBacktests: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + backtest = client.auth_rules.v2.backtests.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Lithic) -> None: + backtest = client.auth_rules.v2.backtests.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + start=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Lithic) -> None: + response = client.auth_rules.v2.backtests.with_raw_response.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backtest = response.parse() + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Lithic) -> None: + with client.auth_rules.v2.backtests.with_streaming_response.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backtest = response.parse() + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.backtests.with_raw_response.create( + auth_rule_token="", + ) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + backtest = client.auth_rules.v2.backtests.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BacktestResults, backtest, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backtest = response.parse() + assert_matches_type(BacktestResults, backtest, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.auth_rules.v2.backtests.with_streaming_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backtest = response.parse() + assert_matches_type(BacktestResults, backtest, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="", + ) + + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `auth_rule_backtest_token` but received ''" + ): + client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + +class TestAsyncBacktests: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncLithic) -> None: + backtest = await async_client.auth_rules.v2.backtests.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: + backtest = await async_client.auth_rules.v2.backtests.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + start=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.backtests.with_raw_response.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backtest = response.parse() + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.backtests.with_streaming_response.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backtest = await response.parse() + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.backtests.with_raw_response.create( + auth_rule_token="", + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + backtest = await async_client.auth_rules.v2.backtests.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BacktestResults, backtest, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backtest = response.parse() + assert_matches_type(BacktestResults, backtest, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.backtests.with_streaming_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backtest = await response.parse() + assert_matches_type(BacktestResults, backtest, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="", + ) + + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `auth_rule_backtest_token` but received ''" + ): + await async_client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) From 9ad6f0bbf8ffc74641f018adaa1cc99e80be1d12 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:03:56 +0000 Subject: [PATCH 6/8] chore(internal): exclude mypy from running on tests (#639) --- mypy.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy.ini b/mypy.ini index 1058d865..b21574d7 100644 --- a/mypy.ini +++ b/mypy.ini @@ -5,7 +5,10 @@ show_error_codes = True # Exclude _files.py because mypy isn't smart enough to apply # the correct type narrowing and as this is an internal module # it's fine to just use Pyright. -exclude = ^(src/lithic/_files\.py|_dev/.*\.py|src/lithic/resources/external_bank_accounts/external_bank_accounts\.py)$ +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ^(src/lithic/_files\.py|_dev/.*\.py|tests/.*|src/lithic/resources/external_bank_accounts/external_bank_accounts\.py)$ strict_equality = True implicit_reexport = True From ec6c6fdfd8e4182339790ffd54dadb2d0dbe69f1 Mon Sep 17 00:00:00 2001 From: Samuel El-Borai Date: Thu, 28 Nov 2024 14:29:00 +0100 Subject: [PATCH 7/8] trigger CI From bfb50648051f9798c451a1e00c2d2cc82ca32c36 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:29:20 +0000 Subject: [PATCH 8/8] release: 0.80.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 21 +++++++++++++++++++++ pyproject.toml | 2 +- src/lithic/_version.py | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 29fa70ce..c3e137b1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.79.1" + ".": "0.80.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a3404721..aa845052 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 0.80.0 (2024-11-28) + +Full Changelog: [v0.79.1...v0.80.0](https://github.com/lithic-com/lithic-python/compare/v0.79.1...v0.80.0) + +### Features + +* **api:** updates to Auth Rules numeric types, new Card Types and Authorization Rule Backtests ([#637](https://github.com/lithic-com/lithic-python/issues/637)) ([6436634](https://github.com/lithic-com/lithic-python/commit/6436634a1a0ccc9b6ec070c2553bdb45ab656dd6)) + + +### Chores + +* **api:** add backtest methods to AuthRules ([#638](https://github.com/lithic-com/lithic-python/issues/638)) ([8125eac](https://github.com/lithic-com/lithic-python/commit/8125eac6b6bb5e448f8641c362a334f0eab5a5fa)) +* **internal:** exclude mypy from running on tests ([#639](https://github.com/lithic-com/lithic-python/issues/639)) ([9ad6f0b](https://github.com/lithic-com/lithic-python/commit/9ad6f0bbf8ffc74641f018adaa1cc99e80be1d12)) +* **internal:** fix compat model_dump method when warnings are passed ([#633](https://github.com/lithic-com/lithic-python/issues/633)) ([eb6c135](https://github.com/lithic-com/lithic-python/commit/eb6c135106a0e11e15aa6245db72a019ea90668e)) +* remove now unused `cached-property` dep ([#636](https://github.com/lithic-com/lithic-python/issues/636)) ([78321ef](https://github.com/lithic-com/lithic-python/commit/78321efbac2e6da20afb852d6bf38fa371751d99)) + + +### Documentation + +* add info log level to readme ([#635](https://github.com/lithic-com/lithic-python/issues/635)) ([aed46f6](https://github.com/lithic-com/lithic-python/commit/aed46f67456c19cf389275607f593e8e269e3237)) + ## 0.79.1 (2024-11-18) Full Changelog: [v0.79.0...v0.79.1](https://github.com/lithic-com/lithic-python/compare/v0.79.0...v0.79.1) diff --git a/pyproject.toml b/pyproject.toml index ed9ddfff..4ad7297d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lithic" -version = "0.79.1" +version = "0.80.0" description = "The official Python library for the lithic API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/lithic/_version.py b/src/lithic/_version.py index d7e9daf8..18af888f 100644 --- a/src/lithic/_version.py +++ b/src/lithic/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "lithic" -__version__ = "0.79.1" # x-release-please-version +__version__ = "0.80.0" # x-release-please-version