diff --git a/CHANGELOG.md b/CHANGELOG.md index 7448ef5..21438e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ Given a version number MAJOR.MINOR.PATCH, increment: ## [Unreleased] +### Added +- request methods +### Changed +- core version ## [2.25.1] - 2024-04-01 ### Fixed diff --git a/README.md b/README.md index 63d35a8..87cf228 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ is as easy as sending a text message to your client! - [WebhookEvents](#process-webhook-events): Manage webhook events - [WebhookEventAttempts](#query-failed-webhook-event-delivery-attempts-information): Query failed webhook event deliveries - [Workspaces](#create-a-new-workspace): Manage your accounts + - [Request](#request): Send a custom request to Stark Bank. This can be used to access features that haven't been mapped yet. - [Handling errors](#handling-errors) - [Help and Feedback](#help-and-feedback) @@ -2588,6 +2589,165 @@ print(workspace) **Note**: the Organization user can only update a workspace with the Workspace ID set. +# request + +This resource allows you to send HTTP requests to StarkBank routes. + +## GET + +You can perform a GET request to any StarkBank route. + +It's possible to get a single resource using its id in the path. + +```python +import starkbank + +example_id = "5155165527080960" +request = starkbank.request.get( + path=f"/invoice/{example_id}" +) + +print(request) +``` + +You can also get the specific resource log, + +```python +import starkbank + +example_id = "5699165527090460" +request = starkbank.request.get( + path=f"/invoice/log/{example_id}", +) + +print(request) +``` + +This same method will be used to list all created items for the requested resource. + +```python +import starkbank + +request = starkbank.request.get( + path="/invoice", + query={"limit": 10, "status": "paid"}, +) + +for item in request["invoices"]: + print(item) +``` + +To list logs, you will use the same logic as for getting a single log. + +```python +import starkbank + +request = starkbank.request.get( + path="/invoice/log", + query={"limit": 10, "status": "paid"}, +) + +for item in request["invoices"]: + print(item) +``` + +You can get a resource file using this method. + +```python +import starkbank + +example_id = "5155165527080960" +pdf = starkbank.request.get( + path=f"/invoice/{example_id}/pdf", +) +with open("request.pdf", "wb") as file: + file.write(pdf) +``` + +## POST + +You can perform a POST request to any StarkBank route. + +This will create an object for each item sent in your request + +**Note**: It's not possible to create multiple resources simultaneously. You need to send separate requests if you want to create multiple resources, such as invoices and boletos. + +```python +import starkbank + +data={ + "invoices": [ + { + "amount": 100, + "name": "Iron Bank S.A.", + "taxId": "20.018.183/0001-80" + }, + { + "amount": 450000, + "name": "Arya Stark.", + "taxId": "012.345.678-90" + } + ] +} +request = starkbank.request.post( + path="/invoice", + body=data, +) +print(request) +``` + +## patch + +You can perform a PATCH request to any StarkBank route. + +It's possible to update a single item of a StarkBank resource. +```python +import starkbank + +example_id = "5155165527080960" +request = starkbank.request.patch( + path=f"/invoice/{example_id}", + body={"amount": 0}, +) +print(request) +``` + +## PUT + +You can perform a PUT request to any StarkBank route. + +It's possible to put a single item of a StarkBank resource. +```python +import starkbank + +data = { + "profiles": [ + { + "interval": "day", + "delay": 0 + } + ] +} +request = starkbank.request.put( + path="/split-profile", + body=data, +) +print(request) +``` +## DELETE + +You can perform a DELETE request to any StarkBank route. + +It's possible to delete a single item of a StarkBank resource. +```python +import starkbank + +example_id = "5155165527080960" +request = starkbank.request.delete( + path=f"/transfer/{example_id}", +) +print(request) +``` # Handling errors The SDK may raise one of four types of errors: __InputErrors__, __InternalServerError__, __UnknownError__, __InvalidSignatureError__ diff --git a/requirements.txt b/requirements.txt index 6e252e3..fc01935 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -starkcore==0.2.1 +starkcore==0.3.2 diff --git a/starkbank/__init__.py b/starkbank/__init__.py index 532aa5c..d070873 100644 --- a/starkbank/__init__.py +++ b/starkbank/__init__.py @@ -110,3 +110,6 @@ from . import splitprofile from .splitprofile.__splitprofile import SplitProfile + +from . import request + diff --git a/starkbank/request/__init__.py b/starkbank/request/__init__.py new file mode 100644 index 0000000..283c3a0 --- /dev/null +++ b/starkbank/request/__init__.py @@ -0,0 +1 @@ +from .__request import (get, post, patch, put, delete) diff --git a/starkbank/request/__request.py b/starkbank/request/__request.py new file mode 100644 index 0000000..22fa28f --- /dev/null +++ b/starkbank/request/__request.py @@ -0,0 +1,100 @@ +from ..utils import rest + + +def get(path, query=None, user=None): + """# Retrieve any StarkBank resource + Receive a json of resources previously created in StarkBank's API + ## Parameters (required): + - path [string]: StarkBank resource's route. ex: "/invoice/" + - query [dict, default None]: Query parameters. ex: {"limit": 1, "status": paid} + ## Parameters (optional): + - user [Organization/Project object, default None]: Organization or Project object. Not necessary if starkbank.user + was set before function call + ## Return: + - dict of StarkBank objects with updated attributes + """ + return rest.get_raw( + path=path, + query=query, + user=user + ) + + +def post(path, body=None, query=None, user=None): + """# Create any StarkBank resource + Send a list of jsons and create any StarkBank resource objects + ## Parameters (required): + - path [string]: StarkBank resource's route. ex: "/invoice/" + - body [dict]: request parameters. ex: {"invoices": [{"amount": 100, "name": "Iron Bank S.A.", "taxId": "20.018.183/0001-80"}]} + ## Parameters (optional): + - user [Organization/Project object, default None]: Organization or Project object. Not necessary if starkbank.user + was set before function call + - query [dict, default None]: Query parameters. ex: {"limit": 1, "status": paid} + ## Return: + - list of resources jsons with updated attributes + """ + return rest.post_raw( + path=path, + payload=body, + query=query, + user=user + ) + + +def patch(path, body=None, user=None): + """# Update any StarkBank resource + Send a json with parameters of a single StarkBank resource object and update it + ## Parameters (required): + - path [string]: StarkBank resource's route. ex: "/invoice/5699165527090460" + - body [dict]: request parameters. ex: {"amount": 100} + ## Parameters (optional): + - user [Organization/Project object, default None]: Organization or Project object. Not necessary if starkbank.user + was set before function call + ## Return: + - json of the resource with updated attributes + """ + return rest.patch_raw( + path=path, + payload=body, + user=user + ) + + +def put(path, body=None, user=None): + """# Put any StarkBank resource + Send a json with parameters of a single StarkBank resource object and create it, if the resource alredy exists, + you will update it. + ## Parameters (required): + - path [string]: StarkBank resource's route. ex: "/invoice" + - body [dict]: request parameters. ex: {"amount": 100} + ## Parameters (optional): + - user [Organization/Project object, default None]: Organization or Project object. Not necessary if starkbank.user + was set before function call + ## Return: + - json of the resource with updated attributes + """ + return rest.put_raw( + path=path, + payload=body, + user=user + ) + + +def delete(path, body=None, user=None): + """# Delete any StarkBank resource + Send a json with parameters of a single StarkBank resource object and delete it + you will update it. + ## Parameters (required): + - path [string]: StarkBank resource's route. ex: "/invoice/5699165527090460" + - body [dict]: request parameters. ex: {"amount": 100} + ## Parameters (optional): + - user [Organization/Project object, default None]: Organization or Project object. Not necessary if starkbank.user + was set before function call + ## Return: + - json of the resource with updated attributes + """ + return rest.delete_raw( + path=path, + payload=body, + user=user + ) diff --git a/starkbank/utils/rest.py b/starkbank/utils/rest.py index 0f550a7..4b4a413 100644 --- a/starkbank/utils/rest.py +++ b/starkbank/utils/rest.py @@ -15,3 +15,6 @@ post_raw = set_relay(rest.post_raw) patch_id = set_relay(rest.patch_id) put_multi = set_relay(rest.put_multi) +put_raw = set_relay(rest.put_raw) +patch_raw = set_relay(rest.patch_raw) +delete_raw = set_relay(rest.delete_raw) diff --git a/tests/sdk/test_corporate_invoice.py b/tests/sdk/test_corporate_invoice.py index cb2e411..3ba9a2f 100644 --- a/tests/sdk/test_corporate_invoice.py +++ b/tests/sdk/test_corporate_invoice.py @@ -12,6 +12,7 @@ class TestCorporateInvoiceQuery(TestCase): def test_success(self): invoices = starkbank.corporateinvoice.query( + limit=1, after=date.today() - timedelta(days=100), before=date.today(), ) diff --git a/tests/sdk/test_corporate_withdrawal.py b/tests/sdk/test_corporate_withdrawal.py index fc4b472..b50202b 100644 --- a/tests/sdk/test_corporate_withdrawal.py +++ b/tests/sdk/test_corporate_withdrawal.py @@ -3,11 +3,37 @@ from datetime import date, timedelta from starkbank import CorporateWithdrawal from tests.utils.user import exampleProject -from tests.utils.withdrawal import generateExampleWithdrawalJson +from tests.utils.withdrawal import generateExampleWithdrawalJson, payment_script +import time starkbank.user = exampleProject +class TestCorporateResourcesChecks(TestCase): + + def test_success_corporate_balance(self): + balance = starkbank.corporatebalance.get() + initial_balance = balance.amount + + if balance.amount < 0: + request = payment_script(balance.amount) + i = 0 + while i <= 5: + br_code = starkbank.brcodepayment.get(request[0].id) + if br_code.status == "success" or br_code.status == "failed": + time.sleep(5) + break + time.sleep(10) + i += 1 + balance = starkbank.corporatebalance.get() + if balance.amount == initial_balance: + time.sleep(5) + balance = starkbank.corporatebalance.get() + print("initial_balance:" + str(initial_balance)) + print("final_balance:" + str(balance.amount)) + print("diff:" + str(initial_balance - balance.amount)) + + class TestCorporateWithdrawalQuery(TestCase): def test_success(self): @@ -50,7 +76,7 @@ def test_success(self): example_withdrawal = generateExampleWithdrawalJson() withdrawal = starkbank.corporatewithdrawal.create( withdrawal=CorporateWithdrawal( - amount=example_withdrawal.amount, + amount=100, external_id=example_withdrawal.external_id ) ) diff --git a/tests/sdk/test_request.py b/tests/sdk/test_request.py new file mode 100644 index 0000000..4f712fd --- /dev/null +++ b/tests/sdk/test_request.py @@ -0,0 +1,202 @@ +import starkbank +from datetime import datetime, timedelta +import time +from unittest import TestCase, main +from tests.utils.date import randomPastDate +from tests.utils.user import exampleProject + + +starkbank.user = exampleProject + + +class TestJokerGet(TestCase): + + def test_get(self): + example_id = starkbank.request.get( + path=f'/invoice/', + query={"limit": 1, "status": "paid"}, + )["invoices"][0]["id"] + + request = starkbank.request.get( + path=f'/invoice/{example_id}', + user=exampleProject + ) + self.assertEqual(request["invoice"]["id"], example_id) + + def test_get_pdf(self): + example_id = starkbank.request.get( + path=f'/invoice/', + query={"limit": 10, "status": "paid"} + )["invoices"][0]["id"] + pdf = starkbank.request.get( + path=f'/invoice/{example_id}/pdf', + ) + + self.assertGreater(len(pdf), 1000) + + def test_get_qrcode(self): + example_id = starkbank.request.get( + path=f'/invoice/', + query={"limit": 10, "status": "paid"} + )["invoices"][0]["id"] + + qrcode = starkbank.request.get( + path=f'/invoice/{example_id}/qrcode', + query={"size": 15}, + ) + self.assertGreater(len(qrcode), 1000) + + def test_get_reversal_receipt(self): + example_id = starkbank.request.get( + path=f'/deposit/log/', + query={"limit": 1, "types": "reversed"} + ) + example_id = example_id["logs"][0]["id"] + reversal_pdf = starkbank.request.get( + path=f'/deposit/log/{example_id}/pdf/', + ) + self.assertGreater(len(reversal_pdf), 1000) + + def test_get_page(self): + after = randomPastDate(days=10) + before = datetime.today() + request = starkbank.request.get( + path=f'/invoice/', + query={ + "limit": 10, + "after": after.strftime("%Y-%m-%d"), + "before": before.strftime("%Y-%m-%d"), + "status": "paid" + } + ) + for item in request["invoices"]: + self.assertTrue(after.date() <= datetime.strptime(item["created"], "%Y-%m-%dT%H:%M:%S.%f%z").date() <= (before + timedelta(hours=3)).date()) + self.assertEqual(10, len(request["invoices"])) + + def test_get_pagination(self): + after = randomPastDate(days=10) + before = datetime.today() + total_items = 0 + cursor = None + i = 0 + while i <= 2: + request = starkbank.request.get( + path=f'/invoice/', + query={ + "limit": 10, + "after": after.strftime("%Y-%m-%d"), + "before": before.strftime("%Y-%m-%d"), + "status": "paid", + "cursor": cursor + } + ) + cursor = request["cursor"] + total_items += len(request["invoices"]) + for item in request["invoices"]: + self.assertTrue(after.date() <= datetime.strptime(item["created"], "%Y-%m-%dT%H:%M:%S.%f%z").date() <= (before + timedelta(hours=3)).date()) + if cursor is None: + break + i += 1 + self.assertLessEqual(len(request["invoices"]), 10) + self.assertLessEqual(total_items, 30) + + +class TestJokerPost(TestCase): + + def test_post(self): + data={ + "invoices": [{ + "amount": 100, + "name": "Iron Bank S.A.", + "taxId": "20.018.183/0001-80" + }] + } + request = starkbank.request.post( + path=f'/invoice/', + body=data, + ) + print(request) + + +class TestJokerPatch(TestCase): + + def test_patch(self): + initial_state = starkbank.request.get( + path=f'/invoice/', + query={"limit": 1, "status": "paid"} + ) + example_id = initial_state["invoices"][0]["id"] + amount = initial_state["invoices"][0]["amount"] + + starkbank.request.patch( + path=f'/invoice/{example_id}/', + body={"amount": amount - amount}, + ) + + final_state = starkbank.request.get( + path=f'/invoice/{example_id}', + ) + self.assertEqual(final_state["invoice"]["amount"],0) + + +class TestJokerPut(TestCase): + + def test_put(self): + data = { + "profiles": [ + { + "interval": "day", + "delay": 0 + } + ] + } + starkbank.request.put( + path=f'/split-profile/', + body=data, + ) + + result = starkbank.request.get(path=f'/split-profile/') + + self.assertEqual(result["profiles"][0]["delay"], 0) + self.assertEqual(result["profiles"][0]["interval"], "day") + + +class TestJokerDelete(TestCase): + + def test_delete(self): + future_date = datetime.now().date() + timedelta(days=10) + + data = { + "transfers": [ + { + "amount": 10000, + "name": "Steve Rogers", + "taxId": "330.731.970-10", + "bankCode": "001", + "branchCode": "1234", + "accountNumber": "123456-0", + "accountType": "checking", + "scheduled": future_date.strftime("%Y-%m-%d"), + "externalId": str(int(time.time() * 1000)), + } + ] + } + + create = starkbank.request.post( + path=f'/transfer/', + body=data, + ) + + starkbank.request.delete( + path=f'/transfer/{create["transfers"][0]["id"]}', + ) + + final_status = starkbank.request.get( + path=f'/transfer/{create["transfers"][0]["id"]}', + )["transfer"]["status"] + + self.assertEqual(final_status, 'canceled') + + +if __name__ == '__main__': + main() diff --git a/tests/utils/withdrawal.py b/tests/utils/withdrawal.py index c172828..61abffe 100644 --- a/tests/utils/withdrawal.py +++ b/tests/utils/withdrawal.py @@ -1,8 +1,8 @@ from random import randint -from starkbank import CorporateWithdrawal +import starkbank -example_withdrawal = CorporateWithdrawal( +example_withdrawal = starkbank.CorporateWithdrawal( amount=10, external_id="123" ) @@ -13,3 +13,14 @@ def generateExampleWithdrawalJson(): example_withdrawal.amount = randint(100, 1000) example_withdrawal.description = "Example Withdrawal" return example_withdrawal + + +def payment_script(balance): + data = starkbank.CorporateInvoice(amount=balance * -1) + corporate_invoice = starkbank.corporateinvoice.create(data) + print(corporate_invoice) + payload = [starkbank.BrcodePayment(brcode=corporate_invoice.brcode, tax_id="20.018.183/0001-80", + description="paying debts")] + payments = starkbank.brcodepayment.create(payments=payload) + + return payments