From 8646f81ca810c14f6a570706a697869bf918e428 Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 6 Dec 2023 16:53:04 +0100 Subject: [PATCH 01/47] feat(VerificationAPI): WiP --- sinch/core/clients/sinch_client_base.py | 2 +- .../clients/sinch_client_configuration.py | 7 ++- sinch/core/clients/sinch_client_sync.py | 10 +++- sinch/core/enums.py | 1 + sinch/core/ports/http_transport.py | 2 + sinch/core/signature.py | 45 ++++++++++++++++++ sinch/domains/verification/__init__.py | 46 +++++++++++++++++++ .../verification/endpoints/__init__.py | 0 .../endpoints/get_verification_by_id.py | 0 .../endpoints/get_verification_by_identity.py | 0 .../get_verification_by_reference.py | 0 .../report_verification_using_identity.py | 0 .../endpoints/report_verification_with_id.py | 0 .../endpoints/start_verification.py | 25 ++++++++++ .../endpoints/verification_endpoint.py | 13 ++++++ sinch/domains/verification/exceptions.py | 5 ++ sinch/domains/verification/models/__init__.py | 0 sinch/domains/verification/models/requests.py | 11 +++++ .../domains/verification/models/responses.py | 13 ++++++ tests/conftest.py | 10 ++++ .../verification/test_start_verification.py | 21 +++++++++ tests/integration/test_request_signing.py | 12 +++++ 22 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 sinch/core/signature.py create mode 100644 sinch/domains/verification/__init__.py create mode 100644 sinch/domains/verification/endpoints/__init__.py create mode 100644 sinch/domains/verification/endpoints/get_verification_by_id.py create mode 100644 sinch/domains/verification/endpoints/get_verification_by_identity.py create mode 100644 sinch/domains/verification/endpoints/get_verification_by_reference.py create mode 100644 sinch/domains/verification/endpoints/report_verification_using_identity.py create mode 100644 sinch/domains/verification/endpoints/report_verification_with_id.py create mode 100644 sinch/domains/verification/endpoints/start_verification.py create mode 100644 sinch/domains/verification/endpoints/verification_endpoint.py create mode 100644 sinch/domains/verification/exceptions.py create mode 100644 sinch/domains/verification/models/__init__.py create mode 100644 sinch/domains/verification/models/requests.py create mode 100644 sinch/domains/verification/models/responses.py create mode 100644 tests/e2e/verification/test_start_verification.py create mode 100644 tests/integration/test_request_signing.py diff --git a/sinch/core/clients/sinch_client_base.py b/sinch/core/clients/sinch_client_base.py index 559558b..31c2d7f 100644 --- a/sinch/core/clients/sinch_client_base.py +++ b/sinch/core/clients/sinch_client_base.py @@ -10,7 +10,7 @@ class ClientBase(ABC): """ Sinch abstract base class for concrete Sinch Client implementations. - By default this SDK provides two implementations - sync and async. + By default, this SDK provides two implementations - sync and async. Feel free to utilize any of them for you custom implementation. """ def __init__( diff --git a/sinch/core/clients/sinch_client_configuration.py b/sinch/core/clients/sinch_client_configuration.py index 86736b4..7fe24e9 100644 --- a/sinch/core/clients/sinch_client_configuration.py +++ b/sinch/core/clients/sinch_client_configuration.py @@ -19,14 +19,19 @@ def __init__( logger=None, logger_name: str = None, disable_https=False, - connection_timeout=10 + connection_timeout=10, + verification_key: str = None, + verification_secret: str = None ): self.key_id = key_id self.key_secret = key_secret self.project_id = project_id + self.verification_key = verification_key + self.verification_secret = verification_secret self.connection_timeout = connection_timeout self.auth_origin = "auth.sinch.com" self.numbers_origin = "numbers.api.sinch.com" + self.verification_origin = "verification.api.sinch.com" self._conversation_region = "eu" self._conversation_domain = ".conversation.api.sinch.com" self._sms_region = "us" diff --git a/sinch/core/clients/sinch_client_sync.py b/sinch/core/clients/sinch_client_sync.py index ab05e5e..8aca18c 100644 --- a/sinch/core/clients/sinch_client_sync.py +++ b/sinch/core/clients/sinch_client_sync.py @@ -6,6 +6,7 @@ from sinch.domains.numbers import Numbers from sinch.domains.conversation import Conversation from sinch.domains.sms import SMS +from sinch.domains.verification import Verification class Client(ClientBase): @@ -20,7 +21,9 @@ def __init__( key_secret, project_id, logger_name=None, - logger=None + logger=None, + verification_key: str = None, + verification_secret: str = None ): super().__init__( key_id=key_id, @@ -36,9 +39,12 @@ def __init__( logger_name=logger_name, logger=logger, transport=HTTPTransportRequests(self), - token_manager=TokenManager(self) + token_manager=TokenManager(self), + verification_key=verification_key, + verification_secret=verification_secret ) self.authentication = Authentication(self) self.numbers = Numbers(self) self.conversation = Conversation(self) self.sms = SMS(self) + self.verification = Verification(self) diff --git a/sinch/core/enums.py b/sinch/core/enums.py index 66879fc..c1d63ee 100644 --- a/sinch/core/enums.py +++ b/sinch/core/enums.py @@ -12,3 +12,4 @@ class HTTPMethods(Enum): class HTTPAuthentication(Enum): BASIC = "BASIC" OAUTH = "OAUTH" + SIGNED = "SIGNED" diff --git a/sinch/core/ports/http_transport.py b/sinch/core/ports/http_transport.py index ff3886a..0b28bb5 100644 --- a/sinch/core/ports/http_transport.py +++ b/sinch/core/ports/http_transport.py @@ -26,6 +26,8 @@ def authenticate(self, endpoint, request_data): "Authorization": f"Bearer {token}", "Content-Type": "application/json" } + elif endpoint.HTTP_AUTHENTICATION == HTTPAuthentication.SIGNED.value: + pass return request_data diff --git a/sinch/core/signature.py b/sinch/core/signature.py new file mode 100644 index 0000000..30911dd --- /dev/null +++ b/sinch/core/signature.py @@ -0,0 +1,45 @@ +import hashlib +import hmac +import base64 +from datetime import datetime, timezone +import json + + +class Signature: + def __init__(self, sinch, http_method, request_data, request_uri): + self.sinch = sinch + self.http_method = http_method + self.content_type = 'application/json; charset=UTF-8' + self.request_data = request_data + self.signature_time = datetime.now(timezone.utc).isoformat() + self.request_uri = request_uri + self.authorization_signature = None + + def get_http_headers_with_signature(self): + return { + "Content-Type": self.content_type, + "Authorization": ( + f"Application {self.sinch.configuration.verification_key}:{self.authorization_signature}" + ), + "x-timestamp": self.signature_time + } + + def calculate(self): + b64_encoded_application_secret = base64.b64decode(self.sinch.configuration.verification_secret) + encoded_verification_request = json.dumps(self.request_data).encode() + md5_verification_request = hashlib.md5(encoded_verification_request) + encoded_md5_to_base64_verification_request = base64.b64encode(md5_verification_request.digest()) + + request_timestamp = "x-timestamp:" + self.signature_time + + string_to_sign = ( + self.http_method + '\n' + + encoded_md5_to_base64_verification_request.decode() + '\n' + + self.content_type + '\n' + + request_timestamp + '\n' + + self.request_uri + ) + + self.authorization_signature = base64.b64encode( + hmac.new(b64_encoded_application_secret, string_to_sign.encode(), hashlib.sha256).digest() + ).decode() diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py new file mode 100644 index 0000000..b02b8d7 --- /dev/null +++ b/sinch/domains/verification/__init__.py @@ -0,0 +1,46 @@ +from sinch.domains.verification.endpoints.start_verification import StartVerificationEndpoint +from sinch.domains.verification.models.responses import StartVerificationResponse +from sinch.domains.verification.models.requests import StartVerificationRequest + + +class Verification: + """ + Documentation for the Verification API: https://developers.sinch.com/docs/verification + """ + def __init__(self, sinch): + self._sinch = sinch + + def start( + self, + identity: dict, + method: str, + reference: str = None, + custom: str = None, + flash_call_options: object = None + ) -> StartVerificationResponse: + return self._sinch.configuration.transport.request( + StartVerificationEndpoint( + request_data=StartVerificationRequest( + identity=identity, + method=method, + reference=reference, + custom=custom, + flash_call_options=flash_call_options + ) + ) + ) + + def get_by_id(self): + pass + + def get_by_identity(self): + pass + + def get_by_reference(self): + pass + + def report_using_id(self): + pass + + def report_using_identity(self): + pass diff --git a/sinch/domains/verification/endpoints/__init__.py b/sinch/domains/verification/endpoints/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sinch/domains/verification/endpoints/get_verification_by_id.py b/sinch/domains/verification/endpoints/get_verification_by_id.py new file mode 100644 index 0000000..e69de29 diff --git a/sinch/domains/verification/endpoints/get_verification_by_identity.py b/sinch/domains/verification/endpoints/get_verification_by_identity.py new file mode 100644 index 0000000..e69de29 diff --git a/sinch/domains/verification/endpoints/get_verification_by_reference.py b/sinch/domains/verification/endpoints/get_verification_by_reference.py new file mode 100644 index 0000000..e69de29 diff --git a/sinch/domains/verification/endpoints/report_verification_using_identity.py b/sinch/domains/verification/endpoints/report_verification_using_identity.py new file mode 100644 index 0000000..e69de29 diff --git a/sinch/domains/verification/endpoints/report_verification_with_id.py b/sinch/domains/verification/endpoints/report_verification_with_id.py new file mode 100644 index 0000000..e69de29 diff --git a/sinch/domains/verification/endpoints/start_verification.py b/sinch/domains/verification/endpoints/start_verification.py new file mode 100644 index 0000000..bd390c8 --- /dev/null +++ b/sinch/domains/verification/endpoints/start_verification.py @@ -0,0 +1,25 @@ +from sinch.core.models.http_response import HTTPResponse +from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint +from sinch.core.enums import HTTPAuthentication, HTTPMethods +# from sinch.domains.conversation.models.contact.responses import CreateConversationContactResponse +# from sinch.domains.conversation.models.contact.requests import CreateConversationContactRequest + + +class StartVerificationEndpoint(VerificationEndpoint): + ENDPOINT_URL = "{origin}/verification/v1/verifications" + HTTP_METHOD = HTTPMethods.POST.value + HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value + + def __init__(self, request_data): + self.request_data = request_data + + def build_url(self, sinch): + return self.ENDPOINT_URL.format( + origin=sinch.configuration.verification_origin, + ) + + def request_body(self): + return self.request_data.as_json() + + def handle_response(self, response: HTTPResponse): + pass diff --git a/sinch/domains/verification/endpoints/verification_endpoint.py b/sinch/domains/verification/endpoints/verification_endpoint.py new file mode 100644 index 0000000..5a95bc0 --- /dev/null +++ b/sinch/domains/verification/endpoints/verification_endpoint.py @@ -0,0 +1,13 @@ +from sinch.core.models.http_response import HTTPResponse +from sinch.core.endpoint import HTTPEndpoint +from sinch.domains.verification.exceptions import VerificationException + + +class VerificationEndpoint(HTTPEndpoint): + def handle_response(self, response: HTTPResponse): + if response.status_code >= 400: + raise VerificationException( + message=response.body["error"].get("message"), + response=response, + is_from_server=True + ) diff --git a/sinch/domains/verification/exceptions.py b/sinch/domains/verification/exceptions.py new file mode 100644 index 0000000..91d913a --- /dev/null +++ b/sinch/domains/verification/exceptions.py @@ -0,0 +1,5 @@ +from sinch.core.exceptions import SinchException + + +class VerificationException(SinchException): + pass diff --git a/sinch/domains/verification/models/__init__.py b/sinch/domains/verification/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sinch/domains/verification/models/requests.py b/sinch/domains/verification/models/requests.py new file mode 100644 index 0000000..a045e03 --- /dev/null +++ b/sinch/domains/verification/models/requests.py @@ -0,0 +1,11 @@ +from dataclasses import dataclass +from sinch.core.models.base_model import SinchRequestBaseModel + + +@dataclass +class StartVerificationRequest(SinchRequestBaseModel): + identity: dict + method: str + reference: str + custom: str + flash_call_options: object diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py new file mode 100644 index 0000000..d26e9c6 --- /dev/null +++ b/sinch/domains/verification/models/responses.py @@ -0,0 +1,13 @@ +from dataclasses import dataclass +from typing import List + +from sinch.core.models.base_model import SinchBaseModel +from sinch.domains.numbers.models import Number + + +@dataclass +class StartVerificationResponse(SinchBaseModel): + id: str + method: str + sms: dict + _links: list diff --git a/tests/conftest.py b/tests/conftest.py index 4bf19fa..438543e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -120,6 +120,16 @@ def origin_phone_number(): return os.getenv("ORIGIN_PHONE_NUMBER") +@pytest.fixture +def verification_key(): + return os.getenv("VERIFICATION_KEY") + + +@pytest.fixture +def verification_secret(): + return os.getenv("VERIFICATION_SECRET") + + @pytest.fixture def app_id(): return os.getenv("APP_ID") diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py new file mode 100644 index 0000000..17a8710 --- /dev/null +++ b/tests/e2e/verification/test_start_verification.py @@ -0,0 +1,21 @@ +from sinch.domains.verification.models.responses import StartVerificationResponse + + +def test_start_verification( + sinch_client_sync, + phone_number, + verification_key, + verification_secret +): + sinch_client_sync.configuration.verification_key = verification_key + sinch_client_sync.configuration.verification_secret = verification_secret + + verification_response = sinch_client_sync.verification.start( + method="sms", + identity={ + "type": "number", + "endpoint": phone_number + } + ) + + assert isinstance(verification_response, StartVerificationResponse) diff --git a/tests/integration/test_request_signing.py b/tests/integration/test_request_signing.py new file mode 100644 index 0000000..fb67631 --- /dev/null +++ b/tests/integration/test_request_signing.py @@ -0,0 +1,12 @@ +from sinch.core.signature import Signature + + +def test_verification_signature(sinch_client_sync): + signature = Signature( + sinch_client_sync, + http_method="POST", + request_data={}, + request_uri="/verification/v1/verifications" + ) + signature.calculate() + assert signature.authorization_signature From 31b75d9a3c46c79b42362b3f542d4438fdb758aa Mon Sep 17 00:00:00 2001 From: 650elx Date: Mon, 11 Dec 2023 01:12:13 +0100 Subject: [PATCH 02/47] feat(verification): WiP --- sinch/core/endpoint.py | 3 + sinch/core/ports/http_transport.py | 13 +- sinch/core/signature.py | 4 +- sinch/domains/verification/__init__.py | 112 ++++++++++++++++-- .../report_verification_using_identity.py | 27 +++++ .../endpoints/start_verification.py | 12 +- tests/conftest.py | 13 ++ ...test_report_verification_using_identity.py | 14 +++ tests/integration/test_request_signing.py | 12 +- 9 files changed, 187 insertions(+), 23 deletions(-) create mode 100644 tests/e2e/verification/test_report_verification_using_identity.py diff --git a/sinch/core/endpoint.py b/sinch/core/endpoint.py index c252370..4a375d0 100644 --- a/sinch/core/endpoint.py +++ b/sinch/core/endpoint.py @@ -10,6 +10,9 @@ class HTTPEndpoint(ABC): def __init__(self, project_id, request_data): pass + def get_uri(self): + return self.ENDPOINT_URL.removeprefix("{origin}") + def build_url(self, sinch): return diff --git a/sinch/core/ports/http_transport.py b/sinch/core/ports/http_transport.py index 0b28bb5..34259de 100644 --- a/sinch/core/ports/http_transport.py +++ b/sinch/core/ports/http_transport.py @@ -1,6 +1,7 @@ import aiohttp from abc import ABC from sinch.core.endpoint import HTTPEndpoint +from sinch.core.signature import Signature from sinch.core.models.http_request import HttpRequest from sinch.core.models.http_response import HTTPResponse from sinch.core.enums import HTTPAuthentication @@ -27,7 +28,13 @@ def authenticate(self, endpoint, request_data): "Content-Type": "application/json" } elif endpoint.HTTP_AUTHENTICATION == HTTPAuthentication.SIGNED.value: - pass + signature = Signature( + self.sinch, + endpoint.HTTP_METHOD, + request_data.request_body, + endpoint.get_uri() + ) + request_data.headers = signature.get_http_headers_with_signature() return request_data @@ -46,7 +53,7 @@ def prepare_request(self, endpoint: HTTPEndpoint) -> HttpRequest: ) def handle_response(self, endpoint: HTTPEndpoint, http_response: HTTPResponse): - if http_response.status_code == 401: + if http_response.status_code == 401 and endpoint.HTTP_AUTHENTICATION == HTTPAuthentication.OAUTH.value: self.sinch.configuration.token_manager.handle_invalid_token(http_response) if self.sinch.configuration.token_manager.token_state == TokenState.EXPIRED: return self.request(endpoint=endpoint) @@ -74,7 +81,7 @@ async def authenticate(self, endpoint, request_data): return request_data async def handle_response(self, endpoint: HTTPEndpoint, http_response: HTTPResponse): - if http_response.status_code == 401: + if http_response.status_code == 401 and endpoint.HTTP_AUTHENTICATION == HTTPAuthentication.OAUTH.value: self.sinch.configuration.token_manager.handle_invalid_token(http_response) if self.sinch.configuration.token_manager.token_state == TokenState.EXPIRED: return await self.request(endpoint=endpoint) diff --git a/sinch/core/signature.py b/sinch/core/signature.py index 30911dd..0831505 100644 --- a/sinch/core/signature.py +++ b/sinch/core/signature.py @@ -16,6 +16,8 @@ def __init__(self, sinch, http_method, request_data, request_uri): self.authorization_signature = None def get_http_headers_with_signature(self): + if not self.authorization_signature: + self.calculate() return { "Content-Type": self.content_type, "Authorization": ( @@ -26,7 +28,7 @@ def get_http_headers_with_signature(self): def calculate(self): b64_encoded_application_secret = base64.b64decode(self.sinch.configuration.verification_secret) - encoded_verification_request = json.dumps(self.request_data).encode() + encoded_verification_request = self.request_data.encode() md5_verification_request = hashlib.md5(encoded_verification_request) encoded_md5_to_base64_verification_request = base64.b64encode(md5_verification_request.digest()) diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index b02b8d7..9aa1444 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -1,6 +1,29 @@ from sinch.domains.verification.endpoints.start_verification import StartVerificationEndpoint -from sinch.domains.verification.models.responses import StartVerificationResponse -from sinch.domains.verification.models.requests import StartVerificationRequest +from sinch.domains.verification.endpoints.report_verification_using_identity import ( + ReportVerificationUsingIdentityEndpoint +) +from sinch.domains.verification.endpoints.report_verification_with_id import ( + ReportVerificationWithIdEndpoint +) +from sinch.domains.verification.endpoints.get_verification_by_id import ( + GetVerificationByIdEndpoint +) +from sinch.domains.verification.endpoints.get_verification_by_identity import ( + GetVerificationByIdentityEndpoint +) +from sinch.domains.verification.endpoints.get_verification_by_reference import ( + GetVerificationByReferenceEndpoint +) +from sinch.domains.verification.models.responses import ( + StartVerificationResponse, + ReportVerificationUsingIdentityResponse, + ReportVerificationWithIdResponse, +) +from sinch.domains.verification.models.requests import ( + StartVerificationRequest, + ReportVerificationUsingIdentityRequest, + ReportVerificationWithIdRequest, +) class Verification: @@ -30,17 +53,82 @@ def start( ) ) - def get_by_id(self): - pass + def report_using_id( + self, + endpoint, + method, + sms_code=None, + flash_call_cli=None, + callout=None + ) -> ReportVerificationUsingIdentityResponse: + return self._sinch.configuration.transport.request( + ReportVerificationUsingIdentityEndpoint( + request_data=ReportVerificationUsingIdentityResponse( + endpoint, + method, + sms_code, + flash_call_cli, + callout + ) + ) + ) - def get_by_identity(self): - pass + def report_using_identity( + self, + endpoint, + method, + sms_code=None, + flash_call_cli=None, + callout=None + ) -> ReportVerificationUsingIdentityResponse: + return self._sinch.configuration.transport.request( + ReportVerificationUsingIdentityEndpoint( + request_data=ReportVerificationUsingIdentityResponse( + endpoint, + method, + sms_code, + flash_call_cli, + callout + ) + ) + ) - def get_by_reference(self): - pass + def get_by_reference(self, reference): + return self._sinch.configuration.transport.request( + ReportVerificationUsingIdentityEndpoint( + request_data=ReportVerificationUsingIdentityResponse( + endpoint, + method, + sms_code, + flash_call_cli, + callout + ) + ) + ) - def report_using_id(self): - pass + def get_by_id(self, id) -> : + return self._sinch.configuration.transport.request( + ReportVerificationUsingIdentityEndpoint( + request_data=ReportVerificationUsingIdentityResponse( + endpoint, + method, + sms_code, + flash_call_cli, + callout + ) + ) + ) + + def get_by_identity(self, id) -> : + return self._sinch.configuration.transport.request( + ReportVerificationUsingIdentityEndpoint( + request_data=ReportVerificationUsingIdentityResponse( + endpoint, + method, + sms_code, + flash_call_cli, + callout + ) + ) + ) - def report_using_identity(self): - pass diff --git a/sinch/domains/verification/endpoints/report_verification_using_identity.py b/sinch/domains/verification/endpoints/report_verification_using_identity.py index e69de29..a908aca 100644 --- a/sinch/domains/verification/endpoints/report_verification_using_identity.py +++ b/sinch/domains/verification/endpoints/report_verification_using_identity.py @@ -0,0 +1,27 @@ +from sinch.core.models.http_response import HTTPResponse +from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint +from sinch.core.enums import HTTPAuthentication, HTTPMethods +from sinch.domains.verification.models.requests import ReportVerificationUsingIdentityRequest +from sinch.domains.verification.models.responses import ReportVerificationUsingIdentityResponse + + +class ReportVerificationUsingIdentityEndpoint(VerificationEndpoint): + ENDPOINT_URL = "{origin}/verification/v1/verifications/number/{endpoint]" + HTTP_METHOD = HTTPMethods.PUT.value + HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value + + def __init__(self, request_data: ReportVerificationUsingIdentityRequest): + self.request_data = request_data + + def build_url(self, sinch): + return self.ENDPOINT_URL.format( + origin=sinch.configuration.verification_origin, + ) + + def request_body(self): + return self.request_data.as_json() + + def handle_response(self, response: HTTPResponse) -> ReportVerificationUsingIdentityResponse: + return ReportVerificationUsingIdentityResponse( + **response.body + ) diff --git a/sinch/domains/verification/endpoints/start_verification.py b/sinch/domains/verification/endpoints/start_verification.py index bd390c8..0508ff8 100644 --- a/sinch/domains/verification/endpoints/start_verification.py +++ b/sinch/domains/verification/endpoints/start_verification.py @@ -1,8 +1,8 @@ from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods -# from sinch.domains.conversation.models.contact.responses import CreateConversationContactResponse -# from sinch.domains.conversation.models.contact.requests import CreateConversationContactRequest +from sinch.domains.verification.models.requests import StartVerificationRequest +from sinch.domains.verification.models.responses import StartVerificationResponse class StartVerificationEndpoint(VerificationEndpoint): @@ -10,7 +10,7 @@ class StartVerificationEndpoint(VerificationEndpoint): HTTP_METHOD = HTTPMethods.POST.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value - def __init__(self, request_data): + def __init__(self, request_data: StartVerificationRequest): self.request_data = request_data def build_url(self, sinch): @@ -21,5 +21,7 @@ def build_url(self, sinch): def request_body(self): return self.request_data.as_json() - def handle_response(self, response: HTTPResponse): - pass + def handle_response(self, response: HTTPResponse) -> StartVerificationResponse: + return StartVerificationResponse( + **response.body + ) diff --git a/tests/conftest.py b/tests/conftest.py index 438543e..1e15369 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -42,6 +42,7 @@ def configure_origin( templates_origin, auth_origin, sms_origin, + verification_origin, disable_ssl ): if auth_origin: @@ -59,6 +60,9 @@ def configure_origin( if sms_origin: sinch_client.configuration.sms_origin = sms_origin + if verification_origin: + sinch_client.configuration.verification_origin = verification_origin + if disable_ssl: sinch_client.configuration.disable_https = True @@ -100,6 +104,11 @@ def sms_origin(): return os.getenv("SMS_ORIGIN") +@pytest.fixture +def verification_origin(): + return os.getenv("VERIFICATION_ORIGIN") + + @pytest.fixture def templates_origin(): return os.getenv("TEMPLATES_ORIGIN") @@ -294,6 +303,7 @@ def sinch_client_sync( templates_origin, auth_origin, sms_origin, + verification_origin, disable_ssl, project_id ): @@ -308,6 +318,7 @@ def sinch_client_sync( templates_origin, auth_origin, sms_origin, + verification_origin, disable_ssl ) @@ -321,6 +332,7 @@ def sinch_client_async( templates_origin, auth_origin, sms_origin, + verification_origin, disable_ssl, project_id ): @@ -335,5 +347,6 @@ def sinch_client_async( templates_origin, auth_origin, sms_origin, + verification_origin, disable_ssl ) diff --git a/tests/e2e/verification/test_report_verification_using_identity.py b/tests/e2e/verification/test_report_verification_using_identity.py new file mode 100644 index 0000000..349a5d2 --- /dev/null +++ b/tests/e2e/verification/test_report_verification_using_identity.py @@ -0,0 +1,14 @@ +# from sinch.domains.verification.models.responses import StartVerificationResponse + + +def test_report_verification_using_identity( + sinch_client_sync, + phone_number, + verification_key, + verification_secret +): + sinch_client_sync.configuration.verification_key = verification_key + sinch_client_sync.configuration.verification_secret = verification_secret + + verification_response = sinch_client_sync.verification.report_using_identity() + # assert isinstance(verification_response, StartVerificationResponse) diff --git a/tests/integration/test_request_signing.py b/tests/integration/test_request_signing.py index fb67631..8e831a6 100644 --- a/tests/integration/test_request_signing.py +++ b/tests/integration/test_request_signing.py @@ -1,11 +1,19 @@ +import json from sinch.core.signature import Signature -def test_verification_signature(sinch_client_sync): +def test_verification_signature( + sinch_client_sync, + verification_key, + verification_secret +): + sinch_client_sync.configuration.verification_key = verification_key + sinch_client_sync.configuration.verification_secret = verification_secret + signature = Signature( sinch_client_sync, http_method="POST", - request_data={}, + request_data=json.dumps({"test": "test"}), request_uri="/verification/v1/verifications" ) signature.calculate() From a9ee46cbbac0ec587cd18db3e990e91a4096f0ae Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 19 Dec 2023 22:40:38 +0100 Subject: [PATCH 03/47] feat(Verification): WiP --- sinch/domains/verification/__init__.py | 8 ++--- .../endpoints/get_verification_by_id.py | 27 +++++++++++++++++ .../endpoints/get_verification_by_identity.py | 27 +++++++++++++++++ .../get_verification_by_reference.py | 27 +++++++++++++++++ .../endpoints/report_verification_with_id.py | 27 +++++++++++++++++ sinch/domains/verification/models/requests.py | 29 +++++++++++++++++++ .../domains/verification/models/responses.py | 28 ++++++++++++++++-- 7 files changed, 166 insertions(+), 7 deletions(-) diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index 9aa1444..999055b 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -63,7 +63,7 @@ def report_using_id( ) -> ReportVerificationUsingIdentityResponse: return self._sinch.configuration.transport.request( ReportVerificationUsingIdentityEndpoint( - request_data=ReportVerificationUsingIdentityResponse( + request_data=ReportVerificationUsingIdentityRequest( endpoint, method, sms_code, @@ -83,7 +83,7 @@ def report_using_identity( ) -> ReportVerificationUsingIdentityResponse: return self._sinch.configuration.transport.request( ReportVerificationUsingIdentityEndpoint( - request_data=ReportVerificationUsingIdentityResponse( + request_data=ReportVerificationUsingIdentityRequest( endpoint, method, sms_code, @@ -106,7 +106,7 @@ def get_by_reference(self, reference): ) ) - def get_by_id(self, id) -> : + def get_by_id(self, id): return self._sinch.configuration.transport.request( ReportVerificationUsingIdentityEndpoint( request_data=ReportVerificationUsingIdentityResponse( @@ -119,7 +119,7 @@ def get_by_id(self, id) -> : ) ) - def get_by_identity(self, id) -> : + def get_by_identity(self, id): return self._sinch.configuration.transport.request( ReportVerificationUsingIdentityEndpoint( request_data=ReportVerificationUsingIdentityResponse( diff --git a/sinch/domains/verification/endpoints/get_verification_by_id.py b/sinch/domains/verification/endpoints/get_verification_by_id.py index e69de29..6f37da0 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_id.py +++ b/sinch/domains/verification/endpoints/get_verification_by_id.py @@ -0,0 +1,27 @@ +from sinch.core.models.http_response import HTTPResponse +from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint +from sinch.core.enums import HTTPAuthentication, HTTPMethods +from sinch.domains.verification.models.requests import GetVerificationByIdRequest +from sinch.domains.verification.models.responses import GetVerificationByIdResponse + + +class GetVerificationByIdEndpoint(VerificationEndpoint): + ENDPOINT_URL = "{origin}/verification/v1/verifications/id/{id}" + HTTP_METHOD = HTTPMethods.GET.value + HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value + + def __init__(self, request_data: GetVerificationByIdRequest): + self.request_data = request_data + + def build_url(self, sinch): + return self.ENDPOINT_URL.format( + origin=sinch.configuration.verification_origin, + ) + + def request_body(self): + return self.request_data.as_json() + + def handle_response(self, response: HTTPResponse) -> GetVerificationByIdResponse: + return GetVerificationByIdResponse( + **response.body + ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_identity.py b/sinch/domains/verification/endpoints/get_verification_by_identity.py index e69de29..7f38aa3 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_identity.py +++ b/sinch/domains/verification/endpoints/get_verification_by_identity.py @@ -0,0 +1,27 @@ +from sinch.core.models.http_response import HTTPResponse +from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint +from sinch.core.enums import HTTPAuthentication, HTTPMethods +from sinch.domains.verification.models.requests import GetVerificationByIdentityRequest +from sinch.domains.verification.models.responses import GetVerificationByIdentityResponse + + +class GetVerificationByIdentityEndpoint(VerificationEndpoint): + ENDPOINT_URL = "{origin}/verification/v1/verifications" + HTTP_METHOD = HTTPMethods.GET.value + HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value + + def __init__(self, request_data: GetVerificationByIdentityRequest): + self.request_data = request_data + + def build_url(self, sinch): + return self.ENDPOINT_URL.format( + origin=sinch.configuration.verification_origin, + ) + + def request_body(self): + return self.request_data.as_json() + + def handle_response(self, response: HTTPResponse) -> GetVerificationByIdentityResponse: + return GetVerificationByIdentityResponse( + **response.body + ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_reference.py b/sinch/domains/verification/endpoints/get_verification_by_reference.py index e69de29..4b2ebf6 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_reference.py +++ b/sinch/domains/verification/endpoints/get_verification_by_reference.py @@ -0,0 +1,27 @@ +from sinch.core.models.http_response import HTTPResponse +from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint +from sinch.core.enums import HTTPAuthentication, HTTPMethods +from sinch.domains.verification.models.requests import GetVerificationByReferenceRequest +from sinch.domains.verification.models.responses import GetVerificationByReferenceResponse + + +class GetVerificationByReferenceEndpoint(VerificationEndpoint): + ENDPOINT_URL = "{origin}/verification/v1/verifications/id/{reference}" + HTTP_METHOD = HTTPMethods.GET.value + HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value + + def __init__(self, request_data: GetVerificationByReferenceRequest): + self.request_data = request_data + + def build_url(self, sinch): + return self.ENDPOINT_URL.format( + origin=sinch.configuration.verification_origin, + ) + + def request_body(self): + return self.request_data.as_json() + + def handle_response(self, response: HTTPResponse) -> GetVerificationByReferenceResponse: + return GetVerificationByReferenceResponse( + **response.body + ) diff --git a/sinch/domains/verification/endpoints/report_verification_with_id.py b/sinch/domains/verification/endpoints/report_verification_with_id.py index e69de29..a10b755 100644 --- a/sinch/domains/verification/endpoints/report_verification_with_id.py +++ b/sinch/domains/verification/endpoints/report_verification_with_id.py @@ -0,0 +1,27 @@ +from sinch.core.models.http_response import HTTPResponse +from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint +from sinch.core.enums import HTTPAuthentication, HTTPMethods +from sinch.domains.verification.models.requests import ReportVerificationUsingIdRequest +from sinch.domains.verification.models.responses import ReportVerificationUsingIdResponse + + +class ReportVerificationWithIdEndpoint(VerificationEndpoint): + ENDPOINT_URL = "{origin}/verification/v1/verifications/number/{id]" + HTTP_METHOD = HTTPMethods.PUT.value + HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value + + def __init__(self, request_data: ReportVerificationUsingIdRequest): + self.request_data = request_data + + def build_url(self, sinch): + return self.ENDPOINT_URL.format( + origin=sinch.configuration.verification_origin, + ) + + def request_body(self): + return self.request_data.as_json() + + def handle_response(self, response: HTTPResponse) -> ReportVerificationUsingIdResponse: + return ReportVerificationUsingIdResponse( + **response.body + ) diff --git a/sinch/domains/verification/models/requests.py b/sinch/domains/verification/models/requests.py index a045e03..a62bbc0 100644 --- a/sinch/domains/verification/models/requests.py +++ b/sinch/domains/verification/models/requests.py @@ -9,3 +9,32 @@ class StartVerificationRequest(SinchRequestBaseModel): reference: str custom: str flash_call_options: object + + +@dataclass +class ReportVerificationUsingIdentityRequest(SinchRequestBaseModel): + endpoint: str + method: str + sms_code: str + flash_call_cli: str + callout: str + + +@dataclass +class ReportVerificationUsingIdRequest(SinchRequestBaseModel): + pass + + +@dataclass +class GetVerificationByReferenceRequest(SinchRequestBaseModel): + pass + + +@dataclass +class GetVerificationByIdRequest(SinchRequestBaseModel): + pass + + +@dataclass +class GetVerificationByIdentityRequest(SinchRequestBaseModel): + pass diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index d26e9c6..3c4f521 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -1,8 +1,5 @@ from dataclasses import dataclass -from typing import List - from sinch.core.models.base_model import SinchBaseModel -from sinch.domains.numbers.models import Number @dataclass @@ -11,3 +8,28 @@ class StartVerificationResponse(SinchBaseModel): method: str sms: dict _links: list + + +@dataclass +class ReportVerificationUsingIdentityResponse(SinchBaseModel): + pass + + +@dataclass +class ReportVerificationUsingIdResponse(SinchBaseModel): + pass + + +@dataclass +class GetVerificationByReferenceResponse(SinchBaseModel): + pass + + +@dataclass +class GetVerificationByIdResponse(SinchBaseModel): + pass + + +@dataclass +class GetVerificationByIdentityResponse(SinchBaseModel): + pass From 5440396659a4bd710b9c5d81c6dcfdb7c78c1634 Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 20 Dec 2023 10:35:41 +0100 Subject: [PATCH 04/47] feat(VerificationAPI): domain facade updated --- sinch/domains/verification/__init__.py | 51 ++++++++----------- .../endpoints/get_verification_by_identity.py | 4 +- ...test_report_verification_using_endpoint.py | 21 ++++++++ 3 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 tests/e2e/verification/test_report_verification_using_endpoint.py diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index 999055b..c2e6a2d 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -14,15 +14,26 @@ from sinch.domains.verification.endpoints.get_verification_by_reference import ( GetVerificationByReferenceEndpoint ) +from sinch.domains.verification.endpoints.get_verification_by_id import GetVerificationByIdEndpoint +from sinch.domains.verification.endpoints.get_verification_by_identity import ( + GetVerificationByIdentityEndpoint +) + from sinch.domains.verification.models.responses import ( StartVerificationResponse, ReportVerificationUsingIdentityResponse, - ReportVerificationWithIdResponse, + ReportVerificationUsingIdResponse, + GetVerificationByIdentityResponse, + GetVerificationByIdResponse, + GetVerificationByReferenceResponse ) from sinch.domains.verification.models.requests import ( StartVerificationRequest, ReportVerificationUsingIdentityRequest, - ReportVerificationWithIdRequest, + ReportVerificationUsingIdRequest, + GetVerificationByIdentityRequest, + GetVerificationByIdRequest, + GetVerificationByReferenceRequest ) @@ -93,42 +104,24 @@ def report_using_identity( ) ) - def get_by_reference(self, reference): + def get_by_reference(self, reference) -> GetVerificationByReferenceResponse: return self._sinch.configuration.transport.request( - ReportVerificationUsingIdentityEndpoint( - request_data=ReportVerificationUsingIdentityResponse( - endpoint, - method, - sms_code, - flash_call_cli, - callout - ) + GetVerificationByReferenceEndpoint( + request_data=GetVerificationByReferenceRequest() ) ) - def get_by_id(self, id): + def get_by_id(self, id) -> GetVerificationByIdResponse: return self._sinch.configuration.transport.request( - ReportVerificationUsingIdentityEndpoint( - request_data=ReportVerificationUsingIdentityResponse( - endpoint, - method, - sms_code, - flash_call_cli, - callout - ) + GetVerificationByIdEndpoint( + request_data=GetVerificationByIdRequest() ) ) - def get_by_identity(self, id): + def get_by_identity(self, id) -> GetVerificationByIdentityResponse: return self._sinch.configuration.transport.request( - ReportVerificationUsingIdentityEndpoint( - request_data=ReportVerificationUsingIdentityResponse( - endpoint, - method, - sms_code, - flash_call_cli, - callout - ) + GetVerificationByIdentityEndpoint( + request_data=GetVerificationByIdentityRequest() ) ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_identity.py b/sinch/domains/verification/endpoints/get_verification_by_identity.py index 7f38aa3..54cec8e 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_identity.py +++ b/sinch/domains/verification/endpoints/get_verification_by_identity.py @@ -6,7 +6,7 @@ class GetVerificationByIdentityEndpoint(VerificationEndpoint): - ENDPOINT_URL = "{origin}/verification/v1/verifications" + ENDPOINT_URL = "{origin}/verification/v1/verifications/{method}/number/{endpoint}" HTTP_METHOD = HTTPMethods.GET.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value @@ -16,6 +16,8 @@ def __init__(self, request_data: GetVerificationByIdentityRequest): def build_url(self, sinch): return self.ENDPOINT_URL.format( origin=sinch.configuration.verification_origin, + medthod=self.request_data.method, + endpoint=self.request_data.endpoint ) def request_body(self): diff --git a/tests/e2e/verification/test_report_verification_using_endpoint.py b/tests/e2e/verification/test_report_verification_using_endpoint.py new file mode 100644 index 0000000..85df20a --- /dev/null +++ b/tests/e2e/verification/test_report_verification_using_endpoint.py @@ -0,0 +1,21 @@ +from sinch.domains.verification.models.responses import GetVerificationByIdentityResponse + + +def test_get_report_verification_using_endpoint( + sinch_client_sync, + phone_number, + verification_key, + verification_secret +): + sinch_client_sync.configuration.verification_key = verification_key + sinch_client_sync.configuration.verification_secret = verification_secret + + verification_response = sinch_client_sync.verification.get_by_identity( + method="sms", + identity={ + "type": "number", + "endpoint": phone_number + } + ) + + assert isinstance(verification_response, GetVerificationByIdentityResponse) From 4ddccb68c8eaa04a9a28957d7c3511f7e26f637b Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 20 Dec 2023 13:09:07 +0100 Subject: [PATCH 05/47] feat(Verification): adapt exception structure --- sinch/domains/verification/endpoints/verification_endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sinch/domains/verification/endpoints/verification_endpoint.py b/sinch/domains/verification/endpoints/verification_endpoint.py index 5a95bc0..e7898b6 100644 --- a/sinch/domains/verification/endpoints/verification_endpoint.py +++ b/sinch/domains/verification/endpoints/verification_endpoint.py @@ -7,7 +7,7 @@ class VerificationEndpoint(HTTPEndpoint): def handle_response(self, response: HTTPResponse): if response.status_code >= 400: raise VerificationException( - message=response.body["error"].get("message"), + message=response.body["message"], response=response, is_from_server=True ) From f9fd637c212129a7b220b412093c6ac563bc658e Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 20 Dec 2023 13:25:41 +0100 Subject: [PATCH 06/47] feat(Verification): WiP --- sinch/domains/verification/__init__.py | 16 +++++++++++----- .../endpoints/get_verification_by_identity.py | 3 ++- .../report_verification_using_identity.py | 3 ++- sinch/domains/verification/models/requests.py | 7 ++++--- ...int.py => test_get_report_using_identity.py} | 13 +++++-------- .../test_report_verification_using_id.py | 17 +++++++++++++++++ .../test_report_verification_using_identity.py | 11 +++++++---- 7 files changed, 48 insertions(+), 22 deletions(-) rename tests/e2e/verification/{test_report_verification_using_endpoint.py => test_get_report_using_identity.py} (68%) create mode 100644 tests/e2e/verification/test_report_verification_using_id.py diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index c2e6a2d..a356dfe 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -107,21 +107,27 @@ def report_using_identity( def get_by_reference(self, reference) -> GetVerificationByReferenceResponse: return self._sinch.configuration.transport.request( GetVerificationByReferenceEndpoint( - request_data=GetVerificationByReferenceRequest() + request_data=GetVerificationByReferenceRequest( + reference=reference + ) ) ) def get_by_id(self, id) -> GetVerificationByIdResponse: return self._sinch.configuration.transport.request( GetVerificationByIdEndpoint( - request_data=GetVerificationByIdRequest() + request_data=GetVerificationByIdRequest( + id=id + ) ) ) - def get_by_identity(self, id) -> GetVerificationByIdentityResponse: + def get_by_identity(self, endpoint, method) -> GetVerificationByIdentityResponse: return self._sinch.configuration.transport.request( GetVerificationByIdentityEndpoint( - request_data=GetVerificationByIdentityRequest() + request_data=GetVerificationByIdentityRequest( + endpoint=endpoint, + method=method + ) ) ) - diff --git a/sinch/domains/verification/endpoints/get_verification_by_identity.py b/sinch/domains/verification/endpoints/get_verification_by_identity.py index 54cec8e..70918bb 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_identity.py +++ b/sinch/domains/verification/endpoints/get_verification_by_identity.py @@ -16,7 +16,7 @@ def __init__(self, request_data: GetVerificationByIdentityRequest): def build_url(self, sinch): return self.ENDPOINT_URL.format( origin=sinch.configuration.verification_origin, - medthod=self.request_data.method, + method=self.request_data.method, endpoint=self.request_data.endpoint ) @@ -24,6 +24,7 @@ def request_body(self): return self.request_data.as_json() def handle_response(self, response: HTTPResponse) -> GetVerificationByIdentityResponse: + super().handle_response(response) return GetVerificationByIdentityResponse( **response.body ) diff --git a/sinch/domains/verification/endpoints/report_verification_using_identity.py b/sinch/domains/verification/endpoints/report_verification_using_identity.py index a908aca..d83cb93 100644 --- a/sinch/domains/verification/endpoints/report_verification_using_identity.py +++ b/sinch/domains/verification/endpoints/report_verification_using_identity.py @@ -6,7 +6,7 @@ class ReportVerificationUsingIdentityEndpoint(VerificationEndpoint): - ENDPOINT_URL = "{origin}/verification/v1/verifications/number/{endpoint]" + ENDPOINT_URL = "{origin}/verification/v1/verifications/number/{endpoint}" HTTP_METHOD = HTTPMethods.PUT.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value @@ -16,6 +16,7 @@ def __init__(self, request_data: ReportVerificationUsingIdentityRequest): def build_url(self, sinch): return self.ENDPOINT_URL.format( origin=sinch.configuration.verification_origin, + endpoint=self.request_data.endpoint ) def request_body(self): diff --git a/sinch/domains/verification/models/requests.py b/sinch/domains/verification/models/requests.py index a62bbc0..8300df2 100644 --- a/sinch/domains/verification/models/requests.py +++ b/sinch/domains/verification/models/requests.py @@ -27,14 +27,15 @@ class ReportVerificationUsingIdRequest(SinchRequestBaseModel): @dataclass class GetVerificationByReferenceRequest(SinchRequestBaseModel): - pass + reference: str @dataclass class GetVerificationByIdRequest(SinchRequestBaseModel): - pass + id: str @dataclass class GetVerificationByIdentityRequest(SinchRequestBaseModel): - pass + endpoint: str + method: str diff --git a/tests/e2e/verification/test_report_verification_using_endpoint.py b/tests/e2e/verification/test_get_report_using_identity.py similarity index 68% rename from tests/e2e/verification/test_report_verification_using_endpoint.py rename to tests/e2e/verification/test_get_report_using_identity.py index 85df20a..8778823 100644 --- a/tests/e2e/verification/test_report_verification_using_endpoint.py +++ b/tests/e2e/verification/test_get_report_using_identity.py @@ -1,7 +1,7 @@ -from sinch.domains.verification.models.responses import GetVerificationByIdentityResponse +from sinch.domains.verification.models.responses import GetVerificationByIdResponse -def test_get_report_verification_using_endpoint( +def test_get_report_verification_using_identity( sinch_client_sync, phone_number, verification_key, @@ -11,11 +11,8 @@ def test_get_report_verification_using_endpoint( sinch_client_sync.configuration.verification_secret = verification_secret verification_response = sinch_client_sync.verification.get_by_identity( - method="sms", - identity={ - "type": "number", - "endpoint": phone_number - } + endpoint=phone_number, + method="sms" ) - assert isinstance(verification_response, GetVerificationByIdentityResponse) + assert isinstance(verification_response, GetVerificationByIdResponse) diff --git a/tests/e2e/verification/test_report_verification_using_id.py b/tests/e2e/verification/test_report_verification_using_id.py new file mode 100644 index 0000000..05c0779 --- /dev/null +++ b/tests/e2e/verification/test_report_verification_using_id.py @@ -0,0 +1,17 @@ +from sinch.domains.verification.models.responses import GetVerificationByIdResponse + + +def test_get_report_verification_using_id( + sinch_client_sync, + phone_number, + verification_key, + verification_secret +): + sinch_client_sync.configuration.verification_key = verification_key + sinch_client_sync.configuration.verification_secret = verification_secret + + verification_response = sinch_client_sync.verification.get_by_id( + id="test" + ) + + assert isinstance(verification_response, GetVerificationByIdResponse) diff --git a/tests/e2e/verification/test_report_verification_using_identity.py b/tests/e2e/verification/test_report_verification_using_identity.py index 349a5d2..3bf1d08 100644 --- a/tests/e2e/verification/test_report_verification_using_identity.py +++ b/tests/e2e/verification/test_report_verification_using_identity.py @@ -1,7 +1,7 @@ -# from sinch.domains.verification.models.responses import StartVerificationResponse +from sinch.domains.verification.models.responses import GetVerificationByIdentityResponse -def test_report_verification_using_identity( +def test_get_report_verification_using_identity( sinch_client_sync, phone_number, verification_key, @@ -10,5 +10,8 @@ def test_report_verification_using_identity( sinch_client_sync.configuration.verification_key = verification_key sinch_client_sync.configuration.verification_secret = verification_secret - verification_response = sinch_client_sync.verification.report_using_identity() - # assert isinstance(verification_response, StartVerificationResponse) + verification_response = sinch_client_sync.verification.report_using_identity( + endpoint=phone_number, + method="sms" + ) + assert isinstance(verification_response, GetVerificationByIdentityResponse) From 1aa36833bbe0d51fefc85006eeeb545b7333e6b0 Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 20 Dec 2023 17:25:01 +0100 Subject: [PATCH 07/47] feat(Signature): WiP --- sinch/core/signature.py | 13 +++++++----- .../endpoints/get_verification_by_identity.py | 3 --- tests/integration/test_request_signing.py | 20 ++++++++++++++++++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/sinch/core/signature.py b/sinch/core/signature.py index 0831505..a8ee91f 100644 --- a/sinch/core/signature.py +++ b/sinch/core/signature.py @@ -2,7 +2,6 @@ import hmac import base64 from datetime import datetime, timezone -import json class Signature: @@ -18,6 +17,7 @@ def __init__(self, sinch, http_method, request_data, request_uri): def get_http_headers_with_signature(self): if not self.authorization_signature: self.calculate() + return { "Content-Type": self.content_type, "Authorization": ( @@ -28,15 +28,18 @@ def get_http_headers_with_signature(self): def calculate(self): b64_encoded_application_secret = base64.b64decode(self.sinch.configuration.verification_secret) - encoded_verification_request = self.request_data.encode() - md5_verification_request = hashlib.md5(encoded_verification_request) - encoded_md5_to_base64_verification_request = base64.b64encode(md5_verification_request.digest()) + if self.request_data: + encoded_verification_request = hashlib.md5(self.request_data.encode()) + encoded_verification_request = base64.b64encode(encoded_verification_request.digest()) + + else: + encoded_verification_request = ''.encode() request_timestamp = "x-timestamp:" + self.signature_time string_to_sign = ( self.http_method + '\n' - + encoded_md5_to_base64_verification_request.decode() + '\n' + + encoded_verification_request.decode() + '\n' + self.content_type + '\n' + request_timestamp + '\n' + self.request_uri diff --git a/sinch/domains/verification/endpoints/get_verification_by_identity.py b/sinch/domains/verification/endpoints/get_verification_by_identity.py index 70918bb..0f3c7d7 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_identity.py +++ b/sinch/domains/verification/endpoints/get_verification_by_identity.py @@ -20,9 +20,6 @@ def build_url(self, sinch): endpoint=self.request_data.endpoint ) - def request_body(self): - return self.request_data.as_json() - def handle_response(self, response: HTTPResponse) -> GetVerificationByIdentityResponse: super().handle_response(response) return GetVerificationByIdentityResponse( diff --git a/tests/integration/test_request_signing.py b/tests/integration/test_request_signing.py index 8e831a6..c8210fe 100644 --- a/tests/integration/test_request_signing.py +++ b/tests/integration/test_request_signing.py @@ -2,7 +2,7 @@ from sinch.core.signature import Signature -def test_verification_signature( +def test_request_signature( sinch_client_sync, verification_key, verification_secret @@ -18,3 +18,21 @@ def test_verification_signature( ) signature.calculate() assert signature.authorization_signature + + +def test_request_signature_using_empty_body( + sinch_client_sync, + verification_key, + verification_secret +): + sinch_client_sync.configuration.verification_key = verification_key + sinch_client_sync.configuration.verification_secret = verification_secret + + signature = Signature( + sinch_client_sync, + http_method="POST", + request_data=None, + request_uri="/verification/v1/verifications" + ) + signature.calculate() + assert signature.authorization_signature From b465ea4d0165dec85b3c8c098d11a39a4b765f79 Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 21 Dec 2023 15:56:32 +0100 Subject: [PATCH 08/47] feat(Verification): GET requests WiP --- sinch/core/endpoint.py | 3 +++ sinch/core/ports/http_transport.py | 2 +- sinch/domains/verification/models/responses.py | 18 +++++++++++++++--- .../test_get_report_using_identity.py | 4 ++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/sinch/core/endpoint.py b/sinch/core/endpoint.py index 4a375d0..746b6f9 100644 --- a/sinch/core/endpoint.py +++ b/sinch/core/endpoint.py @@ -10,6 +10,9 @@ class HTTPEndpoint(ABC): def __init__(self, project_id, request_data): pass + def get_url_without_origin(self, sinch): + return '/' + '/'.join(self.build_url(sinch).split('/')[1:]) + def get_uri(self): return self.ENDPOINT_URL.removeprefix("{origin}") diff --git a/sinch/core/ports/http_transport.py b/sinch/core/ports/http_transport.py index 34259de..6de7c64 100644 --- a/sinch/core/ports/http_transport.py +++ b/sinch/core/ports/http_transport.py @@ -32,7 +32,7 @@ def authenticate(self, endpoint, request_data): self.sinch, endpoint.HTTP_METHOD, request_data.request_body, - endpoint.get_uri() + endpoint.get_url_without_origin(self.sinch) ) request_data.headers = signature.get_http_headers_with_signature() diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 3c4f521..a4b956a 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -21,15 +21,27 @@ class ReportVerificationUsingIdResponse(SinchBaseModel): @dataclass -class GetVerificationByReferenceResponse(SinchBaseModel): +class GetVerificationResponse(SinchBaseModel): + id: str + method: str + status: str + reason: str + price: dict + identity: str + countryId: str + verificationTimestamp: str + + +@dataclass +class GetVerificationByReferenceResponse(GetVerificationResponse): pass @dataclass -class GetVerificationByIdResponse(SinchBaseModel): +class GetVerificationByIdResponse(GetVerificationResponse): pass @dataclass -class GetVerificationByIdentityResponse(SinchBaseModel): +class GetVerificationByIdentityResponse(GetVerificationResponse): pass diff --git a/tests/e2e/verification/test_get_report_using_identity.py b/tests/e2e/verification/test_get_report_using_identity.py index 8778823..cb0f712 100644 --- a/tests/e2e/verification/test_get_report_using_identity.py +++ b/tests/e2e/verification/test_get_report_using_identity.py @@ -1,4 +1,4 @@ -from sinch.domains.verification.models.responses import GetVerificationByIdResponse +from sinch.domains.verification.models.responses import GetVerificationByIdentityResponse def test_get_report_verification_using_identity( @@ -15,4 +15,4 @@ def test_get_report_verification_using_identity( method="sms" ) - assert isinstance(verification_response, GetVerificationByIdResponse) + assert isinstance(verification_response, GetVerificationByIdentityResponse) From 53c9c074e73dcfe5b386b19b477aaa73663510be Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 21 Dec 2023 17:18:30 +0100 Subject: [PATCH 09/47] feat(Verification): GET methods WiP --- .../endpoints/get_verification_by_id.py | 1 + tests/conftest.py | 5 +++++ .../verification/test_get_report_using_id.py | 18 ++++++++++++++++++ .../test_get_report_using_reference.py | 17 +++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 tests/e2e/verification/test_get_report_using_id.py create mode 100644 tests/e2e/verification/test_get_report_using_reference.py diff --git a/sinch/domains/verification/endpoints/get_verification_by_id.py b/sinch/domains/verification/endpoints/get_verification_by_id.py index 6f37da0..14da4b6 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_id.py +++ b/sinch/domains/verification/endpoints/get_verification_by_id.py @@ -16,6 +16,7 @@ def __init__(self, request_data: GetVerificationByIdRequest): def build_url(self, sinch): return self.ENDPOINT_URL.format( origin=sinch.configuration.verification_origin, + id=self.request_data.id ) def request_body(self): diff --git a/tests/conftest.py b/tests/conftest.py index 1e15369..29d0200 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -139,6 +139,11 @@ def verification_secret(): return os.getenv("VERIFICATION_SECRET") +@pytest.fixture +def verification_id(): + return os.getenv("VERIFICATION_ID") + + @pytest.fixture def app_id(): return os.getenv("APP_ID") diff --git a/tests/e2e/verification/test_get_report_using_id.py b/tests/e2e/verification/test_get_report_using_id.py new file mode 100644 index 0000000..8308921 --- /dev/null +++ b/tests/e2e/verification/test_get_report_using_id.py @@ -0,0 +1,18 @@ +from sinch.domains.verification.models.responses import GetVerificationByIdResponse + + +def test_get_report_verification_using_id( + sinch_client_sync, + phone_number, + verification_key, + verification_secret, + verification_id +): + sinch_client_sync.configuration.verification_key = verification_key + sinch_client_sync.configuration.verification_secret = verification_secret + + verification_response = sinch_client_sync.verification.get_by_id( + id=verification_id + ) + + assert isinstance(verification_response, GetVerificationByIdResponse) diff --git a/tests/e2e/verification/test_get_report_using_reference.py b/tests/e2e/verification/test_get_report_using_reference.py new file mode 100644 index 0000000..95d8c66 --- /dev/null +++ b/tests/e2e/verification/test_get_report_using_reference.py @@ -0,0 +1,17 @@ +from sinch.domains.verification.models.responses import GetVerificationByReferenceResponse + + +def test_get_report_verification_using_identity( + sinch_client_sync, + phone_number, + verification_key, + verification_secret +): + sinch_client_sync.configuration.verification_key = verification_key + sinch_client_sync.configuration.verification_secret = verification_secret + + verification_response = sinch_client_sync.verification.get_by_reference( + reference="test" + ) + + assert isinstance(verification_response, GetVerificationByReferenceResponse) From d05360facc5e9c862bd1018583dd0d5f0e295eed Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 21 Dec 2023 18:32:59 +0100 Subject: [PATCH 10/47] feat(Verification): GET methods finished --- .../verification/endpoints/get_verification_by_reference.py | 3 ++- sinch/domains/verification/models/responses.py | 4 ++-- tests/e2e/verification/test_start_verification.py | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sinch/domains/verification/endpoints/get_verification_by_reference.py b/sinch/domains/verification/endpoints/get_verification_by_reference.py index 4b2ebf6..22cada8 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_reference.py +++ b/sinch/domains/verification/endpoints/get_verification_by_reference.py @@ -6,7 +6,7 @@ class GetVerificationByReferenceEndpoint(VerificationEndpoint): - ENDPOINT_URL = "{origin}/verification/v1/verifications/id/{reference}" + ENDPOINT_URL = "{origin}/verification/v1/verifications/reference/{reference}" HTTP_METHOD = HTTPMethods.GET.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value @@ -16,6 +16,7 @@ def __init__(self, request_data: GetVerificationByReferenceRequest): def build_url(self, sinch): return self.ENDPOINT_URL.format( origin=sinch.configuration.verification_origin, + reference=self.request_data.reference ) def request_body(self): diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index a4b956a..130ef71 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -34,7 +34,7 @@ class GetVerificationResponse(SinchBaseModel): @dataclass class GetVerificationByReferenceResponse(GetVerificationResponse): - pass + reference: str @dataclass @@ -44,4 +44,4 @@ class GetVerificationByIdResponse(GetVerificationResponse): @dataclass class GetVerificationByIdentityResponse(GetVerificationResponse): - pass + reference: str diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py index 17a8710..f177696 100644 --- a/tests/e2e/verification/test_start_verification.py +++ b/tests/e2e/verification/test_start_verification.py @@ -15,7 +15,8 @@ def test_start_verification( identity={ "type": "number", "endpoint": phone_number - } + }, + reference="test" ) assert isinstance(verification_response, StartVerificationResponse) From e72af0e6ddf4fa0fc529554c61a0c82dccd089ba Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 22 Dec 2023 15:38:06 +0100 Subject: [PATCH 11/47] feat(Verification): WiP --- .../clients/sinch_client_configuration.py | 2 +- sinch/domains/verification/__init__.py | 36 +++++++------------ .../endpoints/get_verification_by_id.py | 1 + .../get_verification_by_reference.py | 1 + ..._id.py => report_verification_using_id.py} | 9 +++-- .../report_verification_using_identity.py | 4 ++- sinch/domains/verification/models/requests.py | 10 +++--- .../domains/verification/models/responses.py | 32 ++++++++++------- .../test_report_verification_using_id.py | 15 +++++--- ...test_report_verification_using_identity.py | 13 ++++--- .../verification/test_start_verification.py | 2 +- 11 files changed, 68 insertions(+), 57 deletions(-) rename sinch/domains/verification/endpoints/{report_verification_with_id.py => report_verification_using_id.py} (76%) diff --git a/sinch/core/clients/sinch_client_configuration.py b/sinch/core/clients/sinch_client_configuration.py index 7fe24e9..1b36ee8 100644 --- a/sinch/core/clients/sinch_client_configuration.py +++ b/sinch/core/clients/sinch_client_configuration.py @@ -26,7 +26,7 @@ def __init__( self.key_id = key_id self.key_secret = key_secret self.project_id = project_id - self.verification_key = verification_key + self.verifican_key = verification_key self.verification_secret = verification_secret self.connection_timeout = connection_timeout self.auth_origin = "auth.sinch.com" diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index a356dfe..1351ead 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -2,8 +2,8 @@ from sinch.domains.verification.endpoints.report_verification_using_identity import ( ReportVerificationUsingIdentityEndpoint ) -from sinch.domains.verification.endpoints.report_verification_with_id import ( - ReportVerificationWithIdEndpoint +from sinch.domains.verification.endpoints.report_verification_using_id import ( + ReportVerificationUsingIdEndpoint ) from sinch.domains.verification.endpoints.get_verification_by_id import ( GetVerificationByIdEndpoint @@ -50,7 +50,7 @@ def start( method: str, reference: str = None, custom: str = None, - flash_call_options: object = None + flash_call_options: dict = None ) -> StartVerificationResponse: return self._sinch.configuration.transport.request( StartVerificationEndpoint( @@ -66,20 +66,14 @@ def start( def report_using_id( self, - endpoint, - method, - sms_code=None, - flash_call_cli=None, - callout=None - ) -> ReportVerificationUsingIdentityResponse: + id: str, + verification_report_request: dict + ) -> ReportVerificationUsingIdResponse: return self._sinch.configuration.transport.request( - ReportVerificationUsingIdentityEndpoint( - request_data=ReportVerificationUsingIdentityRequest( - endpoint, - method, - sms_code, - flash_call_cli, - callout + ReportVerificationUsingIdEndpoint( + request_data=ReportVerificationUsingIdRequest( + id, + verification_report_request ) ) ) @@ -87,19 +81,13 @@ def report_using_id( def report_using_identity( self, endpoint, - method, - sms_code=None, - flash_call_cli=None, - callout=None + verification_report_request ) -> ReportVerificationUsingIdentityResponse: return self._sinch.configuration.transport.request( ReportVerificationUsingIdentityEndpoint( request_data=ReportVerificationUsingIdentityRequest( endpoint, - method, - sms_code, - flash_call_cli, - callout + verification_report_request ) ) ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_id.py b/sinch/domains/verification/endpoints/get_verification_by_id.py index 14da4b6..862fb02 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_id.py +++ b/sinch/domains/verification/endpoints/get_verification_by_id.py @@ -23,6 +23,7 @@ def request_body(self): return self.request_data.as_json() def handle_response(self, response: HTTPResponse) -> GetVerificationByIdResponse: + super().handle_response(response) return GetVerificationByIdResponse( **response.body ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_reference.py b/sinch/domains/verification/endpoints/get_verification_by_reference.py index 22cada8..abfcd48 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_reference.py +++ b/sinch/domains/verification/endpoints/get_verification_by_reference.py @@ -23,6 +23,7 @@ def request_body(self): return self.request_data.as_json() def handle_response(self, response: HTTPResponse) -> GetVerificationByReferenceResponse: + super().handle_response(response) return GetVerificationByReferenceResponse( **response.body ) diff --git a/sinch/domains/verification/endpoints/report_verification_with_id.py b/sinch/domains/verification/endpoints/report_verification_using_id.py similarity index 76% rename from sinch/domains/verification/endpoints/report_verification_with_id.py rename to sinch/domains/verification/endpoints/report_verification_using_id.py index a10b755..d1c2e55 100644 --- a/sinch/domains/verification/endpoints/report_verification_with_id.py +++ b/sinch/domains/verification/endpoints/report_verification_using_id.py @@ -1,3 +1,4 @@ +import json from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods @@ -5,8 +6,8 @@ from sinch.domains.verification.models.responses import ReportVerificationUsingIdResponse -class ReportVerificationWithIdEndpoint(VerificationEndpoint): - ENDPOINT_URL = "{origin}/verification/v1/verifications/number/{id]" +class ReportVerificationUsingIdEndpoint(VerificationEndpoint): + ENDPOINT_URL = "{origin}/verification/v1/verifications/id/{id}" HTTP_METHOD = HTTPMethods.PUT.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value @@ -16,12 +17,14 @@ def __init__(self, request_data: ReportVerificationUsingIdRequest): def build_url(self, sinch): return self.ENDPOINT_URL.format( origin=sinch.configuration.verification_origin, + id=self.request_data.id ) def request_body(self): - return self.request_data.as_json() + return json.dumps(self.request_data.verification_report_request) def handle_response(self, response: HTTPResponse) -> ReportVerificationUsingIdResponse: + super().handle_response(response) return ReportVerificationUsingIdResponse( **response.body ) diff --git a/sinch/domains/verification/endpoints/report_verification_using_identity.py b/sinch/domains/verification/endpoints/report_verification_using_identity.py index d83cb93..f2d3638 100644 --- a/sinch/domains/verification/endpoints/report_verification_using_identity.py +++ b/sinch/domains/verification/endpoints/report_verification_using_identity.py @@ -1,3 +1,4 @@ +import json from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods @@ -20,9 +21,10 @@ def build_url(self, sinch): ) def request_body(self): - return self.request_data.as_json() + return json.dumps(self.request_data.verification_report_request) def handle_response(self, response: HTTPResponse) -> ReportVerificationUsingIdentityResponse: + super().handle_response(response) return ReportVerificationUsingIdentityResponse( **response.body ) diff --git a/sinch/domains/verification/models/requests.py b/sinch/domains/verification/models/requests.py index 8300df2..78a2c4f 100644 --- a/sinch/domains/verification/models/requests.py +++ b/sinch/domains/verification/models/requests.py @@ -8,21 +8,19 @@ class StartVerificationRequest(SinchRequestBaseModel): method: str reference: str custom: str - flash_call_options: object + flash_call_options: dict @dataclass class ReportVerificationUsingIdentityRequest(SinchRequestBaseModel): endpoint: str - method: str - sms_code: str - flash_call_cli: str - callout: str + verification_report_request: dict @dataclass class ReportVerificationUsingIdRequest(SinchRequestBaseModel): - pass + id: str + verification_report_request: dict @dataclass diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 130ef71..346a330 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -11,37 +11,43 @@ class StartVerificationResponse(SinchBaseModel): @dataclass -class ReportVerificationUsingIdentityResponse(SinchBaseModel): - pass +class VerificationResponse(SinchBaseModel): + id: str + method: str + status: str + reason: str + price: dict + identity: str + countryId: str + verificationTimestamp: str @dataclass -class ReportVerificationUsingIdResponse(SinchBaseModel): - pass +class ReportVerificationUsingIdentityResponse(SinchBaseModel): + reference: str + id: str + method: str + status: str @dataclass -class GetVerificationResponse(SinchBaseModel): +class ReportVerificationUsingIdResponse(SinchBaseModel): + reference: str id: str method: str status: str - reason: str - price: dict - identity: str - countryId: str - verificationTimestamp: str @dataclass -class GetVerificationByReferenceResponse(GetVerificationResponse): +class GetVerificationByReferenceResponse(VerificationResponse): reference: str @dataclass -class GetVerificationByIdResponse(GetVerificationResponse): +class GetVerificationByIdResponse(VerificationResponse): pass @dataclass -class GetVerificationByIdentityResponse(GetVerificationResponse): +class GetVerificationByIdentityResponse(VerificationResponse): reference: str diff --git a/tests/e2e/verification/test_report_verification_using_id.py b/tests/e2e/verification/test_report_verification_using_id.py index 05c0779..c386f99 100644 --- a/tests/e2e/verification/test_report_verification_using_id.py +++ b/tests/e2e/verification/test_report_verification_using_id.py @@ -1,17 +1,24 @@ from sinch.domains.verification.models.responses import GetVerificationByIdResponse -def test_get_report_verification_using_id( +def test_report_verification_using_id_and_sms( sinch_client_sync, phone_number, verification_key, - verification_secret + verification_secret, + verification_id ): sinch_client_sync.configuration.verification_key = verification_key sinch_client_sync.configuration.verification_secret = verification_secret - verification_response = sinch_client_sync.verification.get_by_id( - id="test" + verification_response = sinch_client_sync.verification.report_using_id( + id=verification_id, + verification_report_request={ + "method": "sms", + "sms": { + "code": "1481" + } + } ) assert isinstance(verification_response, GetVerificationByIdResponse) diff --git a/tests/e2e/verification/test_report_verification_using_identity.py b/tests/e2e/verification/test_report_verification_using_identity.py index 3bf1d08..5501134 100644 --- a/tests/e2e/verification/test_report_verification_using_identity.py +++ b/tests/e2e/verification/test_report_verification_using_identity.py @@ -1,7 +1,7 @@ -from sinch.domains.verification.models.responses import GetVerificationByIdentityResponse +from sinch.domains.verification.models.responses import ReportVerificationUsingIdentityResponse -def test_get_report_verification_using_identity( +def test_report_verification_using_identity_and_sms( sinch_client_sync, phone_number, verification_key, @@ -12,6 +12,11 @@ def test_get_report_verification_using_identity( verification_response = sinch_client_sync.verification.report_using_identity( endpoint=phone_number, - method="sms" + verification_report_request={ + "method": "sms", + "sms": { + "code": "1481" + } + } ) - assert isinstance(verification_response, GetVerificationByIdentityResponse) + assert isinstance(verification_response, ReportVerificationUsingIdentityResponse) diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py index f177696..aa37005 100644 --- a/tests/e2e/verification/test_start_verification.py +++ b/tests/e2e/verification/test_start_verification.py @@ -16,7 +16,7 @@ def test_start_verification( "type": "number", "endpoint": phone_number }, - reference="test" + reference="test1" ) assert isinstance(verification_response, StartVerificationResponse) From 0b38e2700f320324ed843fa8b195c2b216ab6771 Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 2 Jan 2024 11:27:20 +0100 Subject: [PATCH 12/47] feat(VerificationAPI): remaining API endpoints, models and tests added --- sinch/core/clients/sinch_client_base.py | 18 +++++++++--------- .../core/clients/sinch_client_configuration.py | 8 ++++---- sinch/core/clients/sinch_client_sync.py | 8 ++++---- sinch/core/signature.py | 4 ++-- sinch/domains/verification/__init__.py | 1 - .../endpoints/get_verification_by_id.py | 3 --- .../endpoints/get_verification_by_reference.py | 3 --- sinch/domains/verification/models/responses.py | 6 +++--- tests/conftest.py | 14 +++++++++----- .../verification/test_get_report_using_id.py | 6 ------ .../test_get_report_using_identity.py | 8 +------- .../test_get_report_using_reference.py | 13 +++---------- .../test_report_verification_using_id.py | 12 +++--------- .../test_report_verification_using_identity.py | 9 ++------- .../verification/test_start_verification.py | 9 ++------- tests/integration/test_request_signing.py | 14 ++------------ 16 files changed, 44 insertions(+), 92 deletions(-) diff --git a/sinch/core/clients/sinch_client_base.py b/sinch/core/clients/sinch_client_base.py index 31c2d7f..8d89413 100644 --- a/sinch/core/clients/sinch_client_base.py +++ b/sinch/core/clients/sinch_client_base.py @@ -21,15 +21,15 @@ def __init__( logger_name=None, logger=None ): - if not key_id or not key_secret or not project_id: - raise ValidationException( - message=( - "key_id, key_secret and project_id are required by the Sinch Client. " - "Those credentials can be obtained from Sinch portal." - ), - is_from_server=False, - response=None - ) + # if not key_id or not key_secret or not project_id: + # raise ValidationException( + # message=( + # "key_id, key_secret and project_id are required by the Sinch Client. " + # "Those credentials can be obtained from Sinch portal." + # ), + # is_from_server=False, + # response=None + # ) self.configuration = Configuration self.authentication = AuthenticationBase diff --git a/sinch/core/clients/sinch_client_configuration.py b/sinch/core/clients/sinch_client_configuration.py index 1b36ee8..74f0931 100644 --- a/sinch/core/clients/sinch_client_configuration.py +++ b/sinch/core/clients/sinch_client_configuration.py @@ -20,14 +20,14 @@ def __init__( logger_name: str = None, disable_https=False, connection_timeout=10, - verification_key: str = None, - verification_secret: str = None + application_key: str = None, + application_secret: str = None ): self.key_id = key_id self.key_secret = key_secret self.project_id = project_id - self.verifican_key = verification_key - self.verification_secret = verification_secret + self.application_key = application_key + self.application_secret = application_secret self.connection_timeout = connection_timeout self.auth_origin = "auth.sinch.com" self.numbers_origin = "numbers.api.sinch.com" diff --git a/sinch/core/clients/sinch_client_sync.py b/sinch/core/clients/sinch_client_sync.py index 8aca18c..3532e63 100644 --- a/sinch/core/clients/sinch_client_sync.py +++ b/sinch/core/clients/sinch_client_sync.py @@ -22,8 +22,8 @@ def __init__( project_id, logger_name=None, logger=None, - verification_key: str = None, - verification_secret: str = None + application_key: str = None, + application_secret: str = None ): super().__init__( key_id=key_id, @@ -40,8 +40,8 @@ def __init__( logger=logger, transport=HTTPTransportRequests(self), token_manager=TokenManager(self), - verification_key=verification_key, - verification_secret=verification_secret + application_key=application_key, + application_secret=application_secret ) self.authentication = Authentication(self) self.numbers = Numbers(self) diff --git a/sinch/core/signature.py b/sinch/core/signature.py index a8ee91f..11e2e3d 100644 --- a/sinch/core/signature.py +++ b/sinch/core/signature.py @@ -21,13 +21,13 @@ def get_http_headers_with_signature(self): return { "Content-Type": self.content_type, "Authorization": ( - f"Application {self.sinch.configuration.verification_key}:{self.authorization_signature}" + f"Application {self.sinch.configuration.application_key}:{self.authorization_signature}" ), "x-timestamp": self.signature_time } def calculate(self): - b64_encoded_application_secret = base64.b64decode(self.sinch.configuration.verification_secret) + b64_encoded_application_secret = base64.b64decode(self.sinch.configuration.application_secret) if self.request_data: encoded_verification_request = hashlib.md5(self.request_data.encode()) encoded_verification_request = base64.b64encode(encoded_verification_request.digest()) diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index 1351ead..3fd40dc 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -18,7 +18,6 @@ from sinch.domains.verification.endpoints.get_verification_by_identity import ( GetVerificationByIdentityEndpoint ) - from sinch.domains.verification.models.responses import ( StartVerificationResponse, ReportVerificationUsingIdentityResponse, diff --git a/sinch/domains/verification/endpoints/get_verification_by_id.py b/sinch/domains/verification/endpoints/get_verification_by_id.py index 862fb02..4a4bee6 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_id.py +++ b/sinch/domains/verification/endpoints/get_verification_by_id.py @@ -19,9 +19,6 @@ def build_url(self, sinch): id=self.request_data.id ) - def request_body(self): - return self.request_data.as_json() - def handle_response(self, response: HTTPResponse) -> GetVerificationByIdResponse: super().handle_response(response) return GetVerificationByIdResponse( diff --git a/sinch/domains/verification/endpoints/get_verification_by_reference.py b/sinch/domains/verification/endpoints/get_verification_by_reference.py index abfcd48..d5796ca 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_reference.py +++ b/sinch/domains/verification/endpoints/get_verification_by_reference.py @@ -19,9 +19,6 @@ def build_url(self, sinch): reference=self.request_data.reference ) - def request_body(self): - return self.request_data.as_json() - def handle_response(self, response: HTTPResponse) -> GetVerificationByReferenceResponse: super().handle_response(response) return GetVerificationByReferenceResponse( diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 346a330..79fa7d7 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -15,11 +15,11 @@ class VerificationResponse(SinchBaseModel): id: str method: str status: str - reason: str price: dict identity: str countryId: str verificationTimestamp: str + reference: str @dataclass @@ -40,7 +40,7 @@ class ReportVerificationUsingIdResponse(SinchBaseModel): @dataclass class GetVerificationByReferenceResponse(VerificationResponse): - reference: str + pass @dataclass @@ -50,4 +50,4 @@ class GetVerificationByIdResponse(VerificationResponse): @dataclass class GetVerificationByIdentityResponse(VerificationResponse): - reference: str + pass diff --git a/tests/conftest.py b/tests/conftest.py index 29d0200..ad42a25 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -130,13 +130,13 @@ def origin_phone_number(): @pytest.fixture -def verification_key(): - return os.getenv("VERIFICATION_KEY") +def application_key(): + return os.getenv("APPLICATION_KEY") @pytest.fixture -def verification_secret(): - return os.getenv("VERIFICATION_SECRET") +def application_secret(): + return os.getenv("APPLICATION_SECRET") @pytest.fixture @@ -303,6 +303,8 @@ def third_int_based_pagination_response(): def sinch_client_sync( key_id, key_secret, + application_key, + application_secret, numbers_origin, conversation_origin, templates_origin, @@ -316,7 +318,9 @@ def sinch_client_sync( Client( key_id=key_id, key_secret=key_secret, - project_id=project_id + project_id=project_id, + application_key=application_key, + application_secret=application_secret ), numbers_origin, conversation_origin, diff --git a/tests/e2e/verification/test_get_report_using_id.py b/tests/e2e/verification/test_get_report_using_id.py index 8308921..8a943a2 100644 --- a/tests/e2e/verification/test_get_report_using_id.py +++ b/tests/e2e/verification/test_get_report_using_id.py @@ -4,15 +4,9 @@ def test_get_report_verification_using_id( sinch_client_sync, phone_number, - verification_key, - verification_secret, verification_id ): - sinch_client_sync.configuration.verification_key = verification_key - sinch_client_sync.configuration.verification_secret = verification_secret - verification_response = sinch_client_sync.verification.get_by_id( id=verification_id ) - assert isinstance(verification_response, GetVerificationByIdResponse) diff --git a/tests/e2e/verification/test_get_report_using_identity.py b/tests/e2e/verification/test_get_report_using_identity.py index cb0f712..a08973b 100644 --- a/tests/e2e/verification/test_get_report_using_identity.py +++ b/tests/e2e/verification/test_get_report_using_identity.py @@ -3,16 +3,10 @@ def test_get_report_verification_using_identity( sinch_client_sync, - phone_number, - verification_key, - verification_secret + phone_number ): - sinch_client_sync.configuration.verification_key = verification_key - sinch_client_sync.configuration.verification_secret = verification_secret - verification_response = sinch_client_sync.verification.get_by_identity( endpoint=phone_number, method="sms" ) - assert isinstance(verification_response, GetVerificationByIdentityResponse) diff --git a/tests/e2e/verification/test_get_report_using_reference.py b/tests/e2e/verification/test_get_report_using_reference.py index 95d8c66..43898ed 100644 --- a/tests/e2e/verification/test_get_report_using_reference.py +++ b/tests/e2e/verification/test_get_report_using_reference.py @@ -1,17 +1,10 @@ from sinch.domains.verification.models.responses import GetVerificationByReferenceResponse -def test_get_report_verification_using_identity( - sinch_client_sync, - phone_number, - verification_key, - verification_secret +def test_get_report_verification_using_reference( + sinch_client_sync ): - sinch_client_sync.configuration.verification_key = verification_key - sinch_client_sync.configuration.verification_secret = verification_secret - verification_response = sinch_client_sync.verification.get_by_reference( - reference="test" + reference="random" ) - assert isinstance(verification_response, GetVerificationByReferenceResponse) diff --git a/tests/e2e/verification/test_report_verification_using_id.py b/tests/e2e/verification/test_report_verification_using_id.py index c386f99..00f7ce0 100644 --- a/tests/e2e/verification/test_report_verification_using_id.py +++ b/tests/e2e/verification/test_report_verification_using_id.py @@ -1,24 +1,18 @@ -from sinch.domains.verification.models.responses import GetVerificationByIdResponse +from sinch.domains.verification.models.responses import ReportVerificationUsingIdResponse def test_report_verification_using_id_and_sms( sinch_client_sync, phone_number, - verification_key, - verification_secret, verification_id ): - sinch_client_sync.configuration.verification_key = verification_key - sinch_client_sync.configuration.verification_secret = verification_secret - verification_response = sinch_client_sync.verification.report_using_id( id=verification_id, verification_report_request={ "method": "sms", "sms": { - "code": "1481" + "code": "2302" } } ) - - assert isinstance(verification_response, GetVerificationByIdResponse) + assert isinstance(verification_response, ReportVerificationUsingIdResponse) diff --git a/tests/e2e/verification/test_report_verification_using_identity.py b/tests/e2e/verification/test_report_verification_using_identity.py index 5501134..860e3ba 100644 --- a/tests/e2e/verification/test_report_verification_using_identity.py +++ b/tests/e2e/verification/test_report_verification_using_identity.py @@ -3,19 +3,14 @@ def test_report_verification_using_identity_and_sms( sinch_client_sync, - phone_number, - verification_key, - verification_secret + phone_number ): - sinch_client_sync.configuration.verification_key = verification_key - sinch_client_sync.configuration.verification_secret = verification_secret - verification_response = sinch_client_sync.verification.report_using_identity( endpoint=phone_number, verification_report_request={ "method": "sms", "sms": { - "code": "1481" + "code": "2302" } } ) diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py index aa37005..24c3c75 100644 --- a/tests/e2e/verification/test_start_verification.py +++ b/tests/e2e/verification/test_start_verification.py @@ -3,20 +3,15 @@ def test_start_verification( sinch_client_sync, - phone_number, - verification_key, - verification_secret + phone_number ): - sinch_client_sync.configuration.verification_key = verification_key - sinch_client_sync.configuration.verification_secret = verification_secret - verification_response = sinch_client_sync.verification.start( method="sms", identity={ "type": "number", "endpoint": phone_number }, - reference="test1" + reference="random" ) assert isinstance(verification_response, StartVerificationResponse) diff --git a/tests/integration/test_request_signing.py b/tests/integration/test_request_signing.py index c8210fe..f4fe4d0 100644 --- a/tests/integration/test_request_signing.py +++ b/tests/integration/test_request_signing.py @@ -3,13 +3,8 @@ def test_request_signature( - sinch_client_sync, - verification_key, - verification_secret + sinch_client_sync ): - sinch_client_sync.configuration.verification_key = verification_key - sinch_client_sync.configuration.verification_secret = verification_secret - signature = Signature( sinch_client_sync, http_method="POST", @@ -21,13 +16,8 @@ def test_request_signature( def test_request_signature_using_empty_body( - sinch_client_sync, - verification_key, - verification_secret + sinch_client_sync ): - sinch_client_sync.configuration.verification_key = verification_key - sinch_client_sync.configuration.verification_secret = verification_secret - signature = Signature( sinch_client_sync, http_method="POST", From 378922490b39d2c10ab60d6ec75b68f40a79784f Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 2 Jan 2024 13:11:53 +0100 Subject: [PATCH 13/47] feat(Verification): validation exception added --- sinch/core/clients/sinch_client_base.py | 11 ----------- sinch/core/ports/http_transport.py | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/sinch/core/clients/sinch_client_base.py b/sinch/core/clients/sinch_client_base.py index 8d89413..52fc231 100644 --- a/sinch/core/clients/sinch_client_base.py +++ b/sinch/core/clients/sinch_client_base.py @@ -1,5 +1,4 @@ from abc import ABC -from sinch.core.exceptions import ValidationException from sinch.core.clients.sinch_client_configuration import Configuration from sinch.domains.authentication import AuthenticationBase from sinch.domains.numbers import NumbersBase @@ -21,16 +20,6 @@ def __init__( logger_name=None, logger=None ): - # if not key_id or not key_secret or not project_id: - # raise ValidationException( - # message=( - # "key_id, key_secret and project_id are required by the Sinch Client. " - # "Those credentials can be obtained from Sinch portal." - # ), - # is_from_server=False, - # response=None - # ) - self.configuration = Configuration self.authentication = AuthenticationBase self.numbers = NumbersBase diff --git a/sinch/core/ports/http_transport.py b/sinch/core/ports/http_transport.py index 6de7c64..22c4f8a 100644 --- a/sinch/core/ports/http_transport.py +++ b/sinch/core/ports/http_transport.py @@ -4,6 +4,7 @@ from sinch.core.signature import Signature from sinch.core.models.http_request import HttpRequest from sinch.core.models.http_response import HTTPResponse +from sinch.core.exceptions import ValidationException from sinch.core.enums import HTTPAuthentication from sinch.core.token_manager import TokenState @@ -16,6 +17,17 @@ def request(self, endpoint: HTTPEndpoint) -> HTTPResponse: pass def authenticate(self, endpoint, request_data): + if endpoint.HTTP_AUTHENTICATION in (HTTPAuthentication.BASIC.value, HTTPAuthentication.OAUTH.value): + if not self.sinch.configuration.key_id or not self.sinch.configuration.key_secret or not self.sinch.configuration.project_id: + raise ValidationException( + message=( + "key_id, key_secret and project_id are required by this API. " + "Those credentials can be obtained from Sinch portal." + ), + is_from_server=False, + response=None + ) + if endpoint.HTTP_AUTHENTICATION == HTTPAuthentication.BASIC.value: request_data.auth = (self.sinch.configuration.key_id, self.sinch.configuration.key_secret) else: @@ -28,6 +40,15 @@ def authenticate(self, endpoint, request_data): "Content-Type": "application/json" } elif endpoint.HTTP_AUTHENTICATION == HTTPAuthentication.SIGNED.value: + if not self.sinch.configuration.application_key or not self.sinch.configuration.application_secret: + raise ValidationException( + message=( + "application key and application secret are required by this API. " + "Those credentials can be obtained from Sinch portal." + ), + is_from_server=False, + response=None + ) signature = Signature( self.sinch, endpoint.HTTP_METHOD, From 38cc6727b901b12e6325e51caef147dcb659d149 Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 2 Jan 2024 13:30:41 +0100 Subject: [PATCH 14/47] refactor(Client): Client classes adapted --- sinch/core/clients/sinch_client_async.py | 16 +++++++--------- sinch/core/clients/sinch_client_base.py | 19 ++++++++++++------- sinch/core/clients/sinch_client_sync.py | 8 +------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/sinch/core/clients/sinch_client_async.py b/sinch/core/clients/sinch_client_async.py index 4f1fd83..df04326 100644 --- a/sinch/core/clients/sinch_client_async.py +++ b/sinch/core/clients/sinch_client_async.py @@ -20,15 +20,10 @@ def __init__( key_secret, project_id, logger_name=None, - logger=None + logger=None, + application_key: str = None, + application_secret: str = None ): - super().__init__( - key_id=key_id, - key_secret=key_secret, - project_id=project_id, - logger_name=logger_name, - logger=logger - ) self.configuration = Configuration( key_id=key_id, key_secret=key_secret, @@ -36,8 +31,11 @@ def __init__( logger_name=logger_name, logger=logger, transport=HTTPTransportAioHTTP(self), - token_manager=TokenManagerAsync(self) + token_manager=TokenManagerAsync(self), + application_secret=application_secret, + application_key=application_key ) + self.authentication = AuthenticationAsync(self) self.numbers = NumbersAsync(self) self.conversation = ConversationAsync(self) diff --git a/sinch/core/clients/sinch_client_base.py b/sinch/core/clients/sinch_client_base.py index 52fc231..7f53fd0 100644 --- a/sinch/core/clients/sinch_client_base.py +++ b/sinch/core/clients/sinch_client_base.py @@ -1,4 +1,4 @@ -from abc import ABC +from abc import ABC, abstractmethod from sinch.core.clients.sinch_client_configuration import Configuration from sinch.domains.authentication import AuthenticationBase from sinch.domains.numbers import NumbersBase @@ -12,19 +12,24 @@ class ClientBase(ABC): By default, this SDK provides two implementations - sync and async. Feel free to utilize any of them for you custom implementation. """ + configuration = Configuration + authentication = AuthenticationBase + numbers = NumbersBase + conversation = ConversationBase + sms = SMSBase + + @abstractmethod def __init__( self, key_id, key_secret, project_id, logger_name=None, - logger=None + logger=None, + application_key: str = None, + application_secret: str = None ): - self.configuration = Configuration - self.authentication = AuthenticationBase - self.numbers = NumbersBase - self.conversation = ConversationBase - self.sms = SMSBase + pass def __repr__(self): return f"Sinch SDK client for project_id: {self.configuration.project_id}" diff --git a/sinch/core/clients/sinch_client_sync.py b/sinch/core/clients/sinch_client_sync.py index 3532e63..0ec939c 100644 --- a/sinch/core/clients/sinch_client_sync.py +++ b/sinch/core/clients/sinch_client_sync.py @@ -25,13 +25,6 @@ def __init__( application_key: str = None, application_secret: str = None ): - super().__init__( - key_id=key_id, - key_secret=key_secret, - project_id=project_id, - logger_name=logger_name, - logger=logger - ) self.configuration = Configuration( key_id=key_id, key_secret=key_secret, @@ -43,6 +36,7 @@ def __init__( application_key=application_key, application_secret=application_secret ) + self.authentication = Authentication(self) self.numbers = Numbers(self) self.conversation = Conversation(self) From 0bfb67907dc9409d2331464a7df4beed3ba95171 Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 2 Jan 2024 14:23:30 +0100 Subject: [PATCH 15/47] feat(Verification): async tests added --- sinch/core/clients/sinch_client_async.py | 2 ++ .../verification/test_get_report_using_id.py | 11 +++++++++++ .../test_get_report_using_identity.py | 11 +++++++++++ .../test_get_report_using_reference.py | 9 +++++++++ .../test_report_verification_using_id.py | 17 +++++++++++++++++ .../test_report_verification_using_identity.py | 16 ++++++++++++++++ .../e2e/verification/test_start_verification.py | 16 ++++++++++++++++ 7 files changed, 82 insertions(+) diff --git a/sinch/core/clients/sinch_client_async.py b/sinch/core/clients/sinch_client_async.py index df04326..a32a223 100644 --- a/sinch/core/clients/sinch_client_async.py +++ b/sinch/core/clients/sinch_client_async.py @@ -6,6 +6,7 @@ from sinch.domains.numbers import NumbersAsync from sinch.domains.conversation import ConversationAsync from sinch.domains.sms import SMSAsync +from sinch.domains.verification import Verification as VerificationAsync class ClientAsync(ClientBase): @@ -40,3 +41,4 @@ def __init__( self.numbers = NumbersAsync(self) self.conversation = ConversationAsync(self) self.sms = SMSAsync(self) + self.verification = VerificationAsync(self) diff --git a/tests/e2e/verification/test_get_report_using_id.py b/tests/e2e/verification/test_get_report_using_id.py index 8a943a2..c3ec5a1 100644 --- a/tests/e2e/verification/test_get_report_using_id.py +++ b/tests/e2e/verification/test_get_report_using_id.py @@ -10,3 +10,14 @@ def test_get_report_verification_using_id( id=verification_id ) assert isinstance(verification_response, GetVerificationByIdResponse) + + +async def test_get_report_verification_using_id_async( + sinch_client_async, + phone_number, + verification_id +): + verification_response = await sinch_client_async.verification.get_by_id( + id=verification_id + ) + assert isinstance(verification_response, GetVerificationByIdResponse) diff --git a/tests/e2e/verification/test_get_report_using_identity.py b/tests/e2e/verification/test_get_report_using_identity.py index a08973b..9eed314 100644 --- a/tests/e2e/verification/test_get_report_using_identity.py +++ b/tests/e2e/verification/test_get_report_using_identity.py @@ -10,3 +10,14 @@ def test_get_report_verification_using_identity( method="sms" ) assert isinstance(verification_response, GetVerificationByIdentityResponse) + + +async def test_get_report_verification_using_identity_async( + sinch_client_async, + phone_number +): + verification_response = await sinch_client_async.verification.get_by_identity( + endpoint=phone_number, + method="sms" + ) + assert isinstance(verification_response, GetVerificationByIdentityResponse) diff --git a/tests/e2e/verification/test_get_report_using_reference.py b/tests/e2e/verification/test_get_report_using_reference.py index 43898ed..f3b1974 100644 --- a/tests/e2e/verification/test_get_report_using_reference.py +++ b/tests/e2e/verification/test_get_report_using_reference.py @@ -8,3 +8,12 @@ def test_get_report_verification_using_reference( reference="random" ) assert isinstance(verification_response, GetVerificationByReferenceResponse) + + +async def test_get_report_verification_using_reference_async( + sinch_client_async +): + verification_response = await sinch_client_async.verification.get_by_reference( + reference="random" + ) + assert isinstance(verification_response, GetVerificationByReferenceResponse) diff --git a/tests/e2e/verification/test_report_verification_using_id.py b/tests/e2e/verification/test_report_verification_using_id.py index 00f7ce0..57ae630 100644 --- a/tests/e2e/verification/test_report_verification_using_id.py +++ b/tests/e2e/verification/test_report_verification_using_id.py @@ -16,3 +16,20 @@ def test_report_verification_using_id_and_sms( } ) assert isinstance(verification_response, ReportVerificationUsingIdResponse) + + +async def test_report_verification_using_id_and_sms_async( + sinch_client_async, + phone_number, + verification_id +): + verification_response = await sinch_client_async.verification.report_using_id( + id=verification_id, + verification_report_request={ + "method": "sms", + "sms": { + "code": "2302" + } + } + ) + assert isinstance(verification_response, ReportVerificationUsingIdResponse) diff --git a/tests/e2e/verification/test_report_verification_using_identity.py b/tests/e2e/verification/test_report_verification_using_identity.py index 860e3ba..c3831fc 100644 --- a/tests/e2e/verification/test_report_verification_using_identity.py +++ b/tests/e2e/verification/test_report_verification_using_identity.py @@ -15,3 +15,19 @@ def test_report_verification_using_identity_and_sms( } ) assert isinstance(verification_response, ReportVerificationUsingIdentityResponse) + + +async def test_report_verification_using_identity_and_sms_async( + sinch_client_async, + phone_number +): + verification_response = await sinch_client_async.verification.report_using_identity( + endpoint=phone_number, + verification_report_request={ + "method": "sms", + "sms": { + "code": "2302" + } + } + ) + assert isinstance(verification_response, ReportVerificationUsingIdentityResponse) diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py index 24c3c75..cf1187e 100644 --- a/tests/e2e/verification/test_start_verification.py +++ b/tests/e2e/verification/test_start_verification.py @@ -15,3 +15,19 @@ def test_start_verification( ) assert isinstance(verification_response, StartVerificationResponse) + + +async def test_start_verification_async( + sinch_client_async, + phone_number +): + verification_response = await sinch_client_async.verification.start( + method="sms", + identity={ + "type": "number", + "endpoint": phone_number + }, + reference="random" + ) + + assert isinstance(verification_response, StartVerificationResponse) From 2f793502fd5c5b8871fca206291a578107912625 Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 2 Jan 2024 15:22:40 +0100 Subject: [PATCH 16/47] feat(Verification): token refresh tests adopted --- sinch/core/ports/http_transport.py | 6 +++++- sinch/domains/verification/__init__.py | 4 ---- tests/integration/test_token_refresh.py | 17 ++++++++++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/sinch/core/ports/http_transport.py b/sinch/core/ports/http_transport.py index 22c4f8a..2b7b52f 100644 --- a/sinch/core/ports/http_transport.py +++ b/sinch/core/ports/http_transport.py @@ -18,7 +18,11 @@ def request(self, endpoint: HTTPEndpoint) -> HTTPResponse: def authenticate(self, endpoint, request_data): if endpoint.HTTP_AUTHENTICATION in (HTTPAuthentication.BASIC.value, HTTPAuthentication.OAUTH.value): - if not self.sinch.configuration.key_id or not self.sinch.configuration.key_secret or not self.sinch.configuration.project_id: + if ( + not self.sinch.configuration.key_id + or not self.sinch.configuration.key_secret + or not self.sinch.configuration.project_id + ): raise ValidationException( message=( "key_id, key_secret and project_id are required by this API. " diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index 3fd40dc..39fd7b3 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -14,10 +14,6 @@ from sinch.domains.verification.endpoints.get_verification_by_reference import ( GetVerificationByReferenceEndpoint ) -from sinch.domains.verification.endpoints.get_verification_by_id import GetVerificationByIdEndpoint -from sinch.domains.verification.endpoints.get_verification_by_identity import ( - GetVerificationByIdentityEndpoint -) from sinch.domains.verification.models.responses import ( StartVerificationResponse, ReportVerificationUsingIdentityResponse, diff --git a/tests/integration/test_token_refresh.py b/tests/integration/test_token_refresh.py index 51c5598..36b97fd 100644 --- a/tests/integration/test_token_refresh.py +++ b/tests/integration/test_token_refresh.py @@ -3,10 +3,15 @@ from sinch.core.models.http_response import HTTPResponse from sinch.domains.authentication.endpoints.oauth import OAuthEndpoint +from sinch.domains.numbers.endpoints.available.list_available_numbers import AvailableNumbersEndpoint from sinch.domains.authentication.exceptions import AuthenticationException -def test_handling_401_without_expiration(sinch_client_sync, auth_token_as_dict): +def test_handling_401_without_expiration( + sinch_client_sync, + auth_token_as_dict, + project_id +): http_response = HTTPResponse( status_code=401, headers={ @@ -26,7 +31,8 @@ def test_handling_401_without_expiration(sinch_client_sync, auth_token_as_dict): def test_handling_401_with_expired_token_gets_invalidated_and_refreshed( sinch_client_sync, auth_token_as_dict, - expired_token_http_response + expired_token_http_response, + project_id ): sinch_client_sync.configuration.token_manager.set_auth_token(auth_token_as_dict) sinch_client_sync.configuration.transport.request = Mock() @@ -34,7 +40,7 @@ def test_handling_401_with_expired_token_gets_invalidated_and_refreshed( transport_response = sinch_client_sync.configuration.transport.handle_response( http_response=expired_token_http_response, - endpoint=OAuthEndpoint() + endpoint=AvailableNumbersEndpoint(project_id, {}) ) assert transport_response == "Token Refresh!" @@ -42,7 +48,8 @@ def test_handling_401_with_expired_token_gets_invalidated_and_refreshed( async def test_handling_401_with_expired_token_gets_refreshed_async( sinch_client_async, auth_token_as_dict, - expired_token_http_response + expired_token_http_response, + project_id ): sinch_client_async.configuration.token_manager.set_auth_token(auth_token_as_dict) sinch_client_async.configuration.transport.request = AsyncMock() @@ -50,6 +57,6 @@ async def test_handling_401_with_expired_token_gets_refreshed_async( transport_response = await sinch_client_async.configuration.transport.handle_response( http_response=expired_token_http_response, - endpoint=OAuthEndpoint() + endpoint=AvailableNumbersEndpoint(project_id, {}) ) assert transport_response == "Token Refresh!" From b074fc046d5f7165a9bca194eb085987b11ac108 Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 2 Jan 2024 15:29:35 +0100 Subject: [PATCH 17/47] fix(pep8) --- sinch/core/ports/http_transport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sinch/core/ports/http_transport.py b/sinch/core/ports/http_transport.py index 618377b..06ff055 100644 --- a/sinch/core/ports/http_transport.py +++ b/sinch/core/ports/http_transport.py @@ -64,7 +64,6 @@ def authenticate(self, endpoint, request_data): ) request_data.headers = signature.get_http_headers_with_signature() - return request_data def prepare_request(self, endpoint: HTTPEndpoint) -> HttpRequest: From 7b5a205bf70bcdc1f3e4db7dc96f165c35383a61 Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 3 Jan 2024 14:10:22 +0100 Subject: [PATCH 18/47] CI:(Verification): map GH secrets --- .github/workflows/run-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 67446e2..3dcd36d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -21,7 +21,9 @@ env: APP_ID: ${{ secrets.APP_ID }} EMPTY_PROJECT_ID: ${{ secrets.EMPTY_PROJECT_ID }} TEMPLATES_ORIGIN: ${{ secrets.TEMPLATES_ORIGIN }} - + APPLICATION_SECRET: ${{ secrets.APPLICATION_SECRET }} + APPLICATION_KEY: ${{ secrets.APPLICATION_KEY }} + VERIFICATION_ID: ${{ secrets.VERIFICATION_ID }} jobs: build: From 67be035618abcb9a5a1bab1ab3d44bad0a0067bf Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 3 Jan 2024 14:22:29 +0100 Subject: [PATCH 19/47] ci(Verification): map GH secret --- .github/workflows/run-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3dcd36d..0bcf118 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,6 +24,7 @@ env: APPLICATION_SECRET: ${{ secrets.APPLICATION_SECRET }} APPLICATION_KEY: ${{ secrets.APPLICATION_KEY }} VERIFICATION_ID: ${{ secrets.VERIFICATION_ID }} + VERIFICATION_ORIGIN: ${{ secrets.VERIFICATION_ORIGIN}} jobs: build: From 5077a0033574151ee3e3e59ef69a89be4445123e Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 3 Jan 2024 15:53:57 +0100 Subject: [PATCH 20/47] test(Verification): async client fixutre fixed --- tests/conftest.py | 6 +++++- tests/integration/test_request_signing.py | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index ad42a25..436c77a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -336,6 +336,8 @@ def sinch_client_sync( def sinch_client_async( key_id, key_secret, + application_key, + application_secret, numbers_origin, conversation_origin, templates_origin, @@ -349,7 +351,9 @@ def sinch_client_async( ClientAsync( key_id=key_id, key_secret=key_secret, - project_id=project_id + project_id=project_id, + application_key=application_key, + application_secret=application_secret ), numbers_origin, conversation_origin, diff --git a/tests/integration/test_request_signing.py b/tests/integration/test_request_signing.py index f4fe4d0..a3245cb 100644 --- a/tests/integration/test_request_signing.py +++ b/tests/integration/test_request_signing.py @@ -13,6 +13,7 @@ def test_request_signature( ) signature.calculate() assert signature.authorization_signature + assert isinstance(signature.authorization_signature, str) def test_request_signature_using_empty_body( @@ -20,9 +21,25 @@ def test_request_signature_using_empty_body( ): signature = Signature( sinch_client_sync, - http_method="POST", + http_method="GET", request_data=None, request_uri="/verification/v1/verifications" ) signature.calculate() assert signature.authorization_signature + assert isinstance(signature.authorization_signature, str) + + +def test_get_headers_with_signature_and_async_client( + sinch_client_async +): + signature = Signature( + sinch_client_async, + http_method="POST", + request_data=None, + request_uri="/verification/v1/verifications" + ) + headers = signature.get_http_headers_with_signature() + assert "x-timestamp" in headers + assert "Authorization" in headers + assert "Content-Type" in headers From f881811c5b0f8260dd9cb298c207f565745d7bad Mon Sep 17 00:00:00 2001 From: 650elx Date: Wed, 3 Jan 2024 16:00:31 +0100 Subject: [PATCH 21/47] fix(Endpoint): remove redundat method --- sinch/core/endpoint.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sinch/core/endpoint.py b/sinch/core/endpoint.py index 746b6f9..d11608b 100644 --- a/sinch/core/endpoint.py +++ b/sinch/core/endpoint.py @@ -13,9 +13,6 @@ def __init__(self, project_id, request_data): def get_url_without_origin(self, sinch): return '/' + '/'.join(self.build_url(sinch).split('/')[1:]) - def get_uri(self): - return self.ENDPOINT_URL.removeprefix("{origin}") - def build_url(self, sinch): return From 90fdaa74a9bfcb6b81ea0c48aebe9d8c89838deb Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 5 Jan 2024 14:29:37 +0100 Subject: [PATCH 22/47] refactor(Verification): ReportVerification model extracted --- sinch/domains/verification/models/responses.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 79fa7d7..83baf48 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -23,19 +23,21 @@ class VerificationResponse(SinchBaseModel): @dataclass -class ReportVerificationUsingIdentityResponse(SinchBaseModel): - reference: str +class ReportVerificationResponse(SinchBaseModel): id: str + reference: str method: str status: str @dataclass -class ReportVerificationUsingIdResponse(SinchBaseModel): - reference: str - id: str - method: str - status: str +class ReportVerificationUsingIdentityResponse(ReportVerificationResponse): + pass + + +@dataclass +class ReportVerificationUsingIdResponse(ReportVerificationResponse): + pass @dataclass From fe2c43876fa7b1f0eea2482df193453c17c0eb85 Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 9 Jan 2024 16:25:06 +0100 Subject: [PATCH 23/47] Support for Python 3.8 removed, 3.12 added --- .github/workflows/run-tests.yml | 2 +- README.md | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 0bcf118..fdf528a 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 diff --git a/README.md b/README.md index c493871..7fc8f92 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ +\ +

[![Sinch Logo](https://developers.sinch.com/static/logo-07afe977d6d9dcd21b066d1612978e5c.svg)](https://www.sinch.com) @@ -8,10 +10,11 @@ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/sinch/sinch-sdk-python/blob/main/LICENSE) - [![Python 3.8](https://img.shields.io/badge/python-3.8-blue.svg)](https://www.python.org/downloads/release/python-380/) [![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-390/) [![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/release/python-3100/) [![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/release/python-3110/) + [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/) +

From 0aa04351b55aa0a8223d7010f62bad2c6de9ab11 Mon Sep 17 00:00:00 2001 From: 650elx Date: Tue, 9 Jan 2024 16:28:43 +0100 Subject: [PATCH 24/47] Revert "Support for Python 3.8 removed, 3.12 added" This reverts commit fe2c43876fa7b1f0eea2482df193453c17c0eb85. --- .github/workflows/run-tests.yml | 2 +- README.md | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index fdf528a..0bcf118 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v3 diff --git a/README.md b/README.md index 7fc8f92..c493871 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -\ -

[![Sinch Logo](https://developers.sinch.com/static/logo-07afe977d6d9dcd21b066d1612978e5c.svg)](https://www.sinch.com) @@ -10,11 +8,10 @@ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/sinch/sinch-sdk-python/blob/main/LICENSE) + [![Python 3.8](https://img.shields.io/badge/python-3.8-blue.svg)](https://www.python.org/downloads/release/python-380/) [![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-390/) [![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/release/python-3100/) [![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/release/python-3110/) - [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/) -

From 6e06e40bc58045f188c2ed1cf91b0d2bd0c632f8 Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 12 Jan 2024 15:37:12 +0100 Subject: [PATCH 25/47] fix(Verification): unified method names added --- sinch/domains/verification/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index 39fd7b3..b701fbd 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -59,7 +59,7 @@ def start( ) ) - def report_using_id( + def report_by_id( self, id: str, verification_report_request: dict @@ -73,7 +73,7 @@ def report_using_id( ) ) - def report_using_identity( + def report_by_identity( self, endpoint, verification_report_request From 3bd60ba43f787e5d8db17d5053978ab80e6dfbf3 Mon Sep 17 00:00:00 2001 From: 650elx Date: Mon, 15 Jan 2024 12:54:36 +0100 Subject: [PATCH 26/47] refactor(Verification): main facade --- sinch/domains/verification/__init__.py | 42 +++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index b701fbd..696d93f 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -32,10 +32,7 @@ ) -class Verification: - """ - Documentation for the Verification API: https://developers.sinch.com/docs/verification - """ +class Verifications: def __init__(self, sinch): self._sinch = sinch @@ -87,6 +84,11 @@ def report_by_identity( ) ) + +class VerificationStatus: + def __init__(self, sinch): + self._sinch = sinch + def get_by_reference(self, reference) -> GetVerificationByReferenceResponse: return self._sinch.configuration.transport.request( GetVerificationByReferenceEndpoint( @@ -114,3 +116,35 @@ def get_by_identity(self, endpoint, method) -> GetVerificationByIdentityResponse ) ) ) + + +class VerificationBase: + """ + Documentation for the Verification API: https://developers.sinch.com/docs/verification/ + """ + def __init__(self, sinch): + self._sinch = sinch + + +class Verification(VerificationBase): + """ + Synchronous version of the Verification Domain + """ + __doc__ += VerificationBase.__doc__ + + def __init__(self, sinch): + super(Verification, self).__init__(sinch) + self.verifications = Verifications(self._sinch) + self.verification_status = VerificationStatus(self._sinch) + + +class VerificationAsync(VerificationBase): + """ + Asynchronous version of the Verification Domain + """ + __doc__ += VerificationBase.__doc__ + + def __init__(self, sinch): + super(VerificationAsync, self).__init__(sinch) + self.verifications = Verifications(self._sinch) + self.verification_status = VerificationStatus(self._sinch) From 0ecbd2d8da9d593b46e15c42d113eb18683851e5 Mon Sep 17 00:00:00 2001 From: 650elx Date: Mon, 15 Jan 2024 13:27:01 +0100 Subject: [PATCH 27/47] test(Verification): tests adapted --- tests/e2e/verification/test_get_report_using_id.py | 4 ++-- tests/e2e/verification/test_get_report_using_identity.py | 4 ++-- tests/e2e/verification/test_get_report_using_reference.py | 4 ++-- tests/e2e/verification/test_report_verification_using_id.py | 4 ++-- .../verification/test_report_verification_using_identity.py | 4 ++-- tests/e2e/verification/test_start_verification.py | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/e2e/verification/test_get_report_using_id.py b/tests/e2e/verification/test_get_report_using_id.py index c3ec5a1..9058aef 100644 --- a/tests/e2e/verification/test_get_report_using_id.py +++ b/tests/e2e/verification/test_get_report_using_id.py @@ -6,7 +6,7 @@ def test_get_report_verification_using_id( phone_number, verification_id ): - verification_response = sinch_client_sync.verification.get_by_id( + verification_response = sinch_client_sync.verification.verification_status.get_by_id( id=verification_id ) assert isinstance(verification_response, GetVerificationByIdResponse) @@ -17,7 +17,7 @@ async def test_get_report_verification_using_id_async( phone_number, verification_id ): - verification_response = await sinch_client_async.verification.get_by_id( + verification_response = await sinch_client_async.verification.verification_status.get_by_id( id=verification_id ) assert isinstance(verification_response, GetVerificationByIdResponse) diff --git a/tests/e2e/verification/test_get_report_using_identity.py b/tests/e2e/verification/test_get_report_using_identity.py index 9eed314..3e197bf 100644 --- a/tests/e2e/verification/test_get_report_using_identity.py +++ b/tests/e2e/verification/test_get_report_using_identity.py @@ -5,7 +5,7 @@ def test_get_report_verification_using_identity( sinch_client_sync, phone_number ): - verification_response = sinch_client_sync.verification.get_by_identity( + verification_response = sinch_client_sync.verification.verification_status.get_by_identity( endpoint=phone_number, method="sms" ) @@ -16,7 +16,7 @@ async def test_get_report_verification_using_identity_async( sinch_client_async, phone_number ): - verification_response = await sinch_client_async.verification.get_by_identity( + verification_response = await sinch_client_async.verification.verification_status.get_by_identity( endpoint=phone_number, method="sms" ) diff --git a/tests/e2e/verification/test_get_report_using_reference.py b/tests/e2e/verification/test_get_report_using_reference.py index f3b1974..f368b35 100644 --- a/tests/e2e/verification/test_get_report_using_reference.py +++ b/tests/e2e/verification/test_get_report_using_reference.py @@ -4,7 +4,7 @@ def test_get_report_verification_using_reference( sinch_client_sync ): - verification_response = sinch_client_sync.verification.get_by_reference( + verification_response = sinch_client_sync.verification.verification_status.get_by_reference( reference="random" ) assert isinstance(verification_response, GetVerificationByReferenceResponse) @@ -13,7 +13,7 @@ def test_get_report_verification_using_reference( async def test_get_report_verification_using_reference_async( sinch_client_async ): - verification_response = await sinch_client_async.verification.get_by_reference( + verification_response = await sinch_client_async.verification.verification_status.get_by_reference( reference="random" ) assert isinstance(verification_response, GetVerificationByReferenceResponse) diff --git a/tests/e2e/verification/test_report_verification_using_id.py b/tests/e2e/verification/test_report_verification_using_id.py index 57ae630..f197e2e 100644 --- a/tests/e2e/verification/test_report_verification_using_id.py +++ b/tests/e2e/verification/test_report_verification_using_id.py @@ -6,7 +6,7 @@ def test_report_verification_using_id_and_sms( phone_number, verification_id ): - verification_response = sinch_client_sync.verification.report_using_id( + verification_response = sinch_client_sync.verification.verifications.report_by_id( id=verification_id, verification_report_request={ "method": "sms", @@ -23,7 +23,7 @@ async def test_report_verification_using_id_and_sms_async( phone_number, verification_id ): - verification_response = await sinch_client_async.verification.report_using_id( + verification_response = await sinch_client_async.verification.verifications.report_by_id( id=verification_id, verification_report_request={ "method": "sms", diff --git a/tests/e2e/verification/test_report_verification_using_identity.py b/tests/e2e/verification/test_report_verification_using_identity.py index c3831fc..6071d2f 100644 --- a/tests/e2e/verification/test_report_verification_using_identity.py +++ b/tests/e2e/verification/test_report_verification_using_identity.py @@ -5,7 +5,7 @@ def test_report_verification_using_identity_and_sms( sinch_client_sync, phone_number ): - verification_response = sinch_client_sync.verification.report_using_identity( + verification_response = sinch_client_sync.verification.verifications.report_by_identity( endpoint=phone_number, verification_report_request={ "method": "sms", @@ -21,7 +21,7 @@ async def test_report_verification_using_identity_and_sms_async( sinch_client_async, phone_number ): - verification_response = await sinch_client_async.verification.report_using_identity( + verification_response = await sinch_client_async.verification.verifications.report_by_identity( endpoint=phone_number, verification_report_request={ "method": "sms", diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py index cf1187e..2456f88 100644 --- a/tests/e2e/verification/test_start_verification.py +++ b/tests/e2e/verification/test_start_verification.py @@ -5,7 +5,7 @@ def test_start_verification( sinch_client_sync, phone_number ): - verification_response = sinch_client_sync.verification.start( + verification_response = sinch_client_sync.verification.verifications.start( method="sms", identity={ "type": "number", @@ -21,7 +21,7 @@ async def test_start_verification_async( sinch_client_async, phone_number ): - verification_response = await sinch_client_async.verification.start( + verification_response = await sinch_client_async.verification.verifications.start( method="sms", identity={ "type": "number", From 143e4f170cd1dff7a22e0529252c868e41ed7cce Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 18 Jan 2024 01:04:04 +0100 Subject: [PATCH 28/47] fix(Verification): VerificationMethod enum added --- sinch/domains/verification/__init__.py | 4 +++- sinch/domains/verification/enums.py | 8 ++++++++ sinch/domains/verification/models/requests.py | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 sinch/domains/verification/enums.py diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index 696d93f..8a6ed6b 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -31,6 +31,8 @@ GetVerificationByReferenceRequest ) +from sinch.domains.verification.enums import VerificationMethod + class Verifications: def __init__(self, sinch): @@ -39,7 +41,7 @@ def __init__(self, sinch): def start( self, identity: dict, - method: str, + method: VerificationMethod, reference: str = None, custom: str = None, flash_call_options: dict = None diff --git a/sinch/domains/verification/enums.py b/sinch/domains/verification/enums.py new file mode 100644 index 0000000..38dc5f2 --- /dev/null +++ b/sinch/domains/verification/enums.py @@ -0,0 +1,8 @@ +from enum import Enum + + +class VerificationMethod(Enum): + SMS = "sms" + FLASHCALL = "flashCall" + CALLOUT = "callout" + SEAMLESS = "seamless" diff --git a/sinch/domains/verification/models/requests.py b/sinch/domains/verification/models/requests.py index 78a2c4f..f13120a 100644 --- a/sinch/domains/verification/models/requests.py +++ b/sinch/domains/verification/models/requests.py @@ -1,11 +1,12 @@ from dataclasses import dataclass from sinch.core.models.base_model import SinchRequestBaseModel +from sinch.domains.verification.enums import VerificationMethod @dataclass class StartVerificationRequest(SinchRequestBaseModel): identity: dict - method: str + method: VerificationMethod reference: str custom: str flash_call_options: dict From 4c2df891f3b42401795491d530a05d1e49db9036 Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 18 Jan 2024 15:08:30 +0100 Subject: [PATCH 29/47] test(Verification): signature tests improved --- sinch/core/signature.py | 6 +++--- tests/conftest.py | 17 ++++++++++++++- tests/integration/test_request_signing.py | 25 ++++++++++++++++++----- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/sinch/core/signature.py b/sinch/core/signature.py index 11e2e3d..ad099d9 100644 --- a/sinch/core/signature.py +++ b/sinch/core/signature.py @@ -10,7 +10,7 @@ def __init__(self, sinch, http_method, request_data, request_uri): self.http_method = http_method self.content_type = 'application/json; charset=UTF-8' self.request_data = request_data - self.signature_time = datetime.now(timezone.utc).isoformat() + self.signature_timestamp = datetime.now(timezone.utc).isoformat() self.request_uri = request_uri self.authorization_signature = None @@ -23,7 +23,7 @@ def get_http_headers_with_signature(self): "Authorization": ( f"Application {self.sinch.configuration.application_key}:{self.authorization_signature}" ), - "x-timestamp": self.signature_time + "x-timestamp": self.signature_timestamp } def calculate(self): @@ -35,7 +35,7 @@ def calculate(self): else: encoded_verification_request = ''.encode() - request_timestamp = "x-timestamp:" + self.signature_time + request_timestamp = "x-timestamp:" + self.signature_timestamp string_to_sign = ( self.http_method + '\n' diff --git a/tests/conftest.py b/tests/conftest.py index 436c77a..6b84ada 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -159,6 +159,21 @@ def empty_project_id(): return os.getenv("EMPTY_PROJECT_ID") +@pytest.fixture +def verification_request_signature(): + return os.getenv("VERIFICATION_REQUEST_SIGNATURE") + + +@pytest.fixture +def verification_request_with_empty_body_signature(): + return os.getenv("VERIFICATION_REQUEST_WITH_EMPTY_BODY_SIGNATURE") + + +@pytest.fixture +def verification_request_signature_timestamp(): + return os.getenv("VERIFICATION_REQUEST_SIGNATURE_TIMESTAMP") + + @pytest.fixture def http_response(): return HTTPResponse( @@ -179,7 +194,7 @@ def sms_http_response(): return HTTPResponse( status_code=404, body={ - "text": "Nobody expects the Spanish Inquisition!" + "text": "Nobody expects the Spanish Inquisition!" }, headers={ "SAMPLE_HEADER": "test" diff --git a/tests/integration/test_request_signing.py b/tests/integration/test_request_signing.py index a3245cb..65018ad 100644 --- a/tests/integration/test_request_signing.py +++ b/tests/integration/test_request_signing.py @@ -3,35 +3,47 @@ def test_request_signature( - sinch_client_sync + sinch_client_sync, + verification_request_signature, + verification_request_signature_timestamp ): signature = Signature( sinch_client_sync, - http_method="POST", + http_method="GET", request_data=json.dumps({"test": "test"}), request_uri="/verification/v1/verifications" ) + signature.signature_timestamp = verification_request_signature_timestamp signature.calculate() + assert signature.authorization_signature assert isinstance(signature.authorization_signature, str) + assert verification_request_signature == signature.authorization_signature def test_request_signature_using_empty_body( - sinch_client_sync + sinch_client_sync, + verification_request_with_empty_body_signature, + verification_request_signature_timestamp ): signature = Signature( sinch_client_sync, - http_method="GET", + http_method="POST", request_data=None, request_uri="/verification/v1/verifications" ) + signature.signature_timestamp = verification_request_signature_timestamp signature.calculate() + assert signature.authorization_signature assert isinstance(signature.authorization_signature, str) + assert verification_request_with_empty_body_signature == signature.authorization_signature def test_get_headers_with_signature_and_async_client( - sinch_client_async + sinch_client_async, + verification_request_with_empty_body_signature, + verification_request_signature_timestamp ): signature = Signature( sinch_client_async, @@ -39,7 +51,10 @@ def test_get_headers_with_signature_and_async_client( request_data=None, request_uri="/verification/v1/verifications" ) + signature.signature_timestamp = verification_request_signature_timestamp headers = signature.get_http_headers_with_signature() + assert "x-timestamp" in headers assert "Authorization" in headers assert "Content-Type" in headers + assert verification_request_with_empty_body_signature in headers["Authorization"] From 3317bf0c9f74485fc3530647aec3809ba450c0f7 Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 18 Jan 2024 15:35:19 +0100 Subject: [PATCH 30/47] fix(Signature): naming fixed --- sinch/core/signature.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sinch/core/signature.py b/sinch/core/signature.py index ad099d9..4028543 100644 --- a/sinch/core/signature.py +++ b/sinch/core/signature.py @@ -27,7 +27,7 @@ def get_http_headers_with_signature(self): } def calculate(self): - b64_encoded_application_secret = base64.b64decode(self.sinch.configuration.application_secret) + b64_decoded_application_secret = base64.b64decode(self.sinch.configuration.application_secret) if self.request_data: encoded_verification_request = hashlib.md5(self.request_data.encode()) encoded_verification_request = base64.b64encode(encoded_verification_request.digest()) @@ -46,5 +46,5 @@ def calculate(self): ) self.authorization_signature = base64.b64encode( - hmac.new(b64_encoded_application_secret, string_to_sign.encode(), hashlib.sha256).digest() + hmac.new(b64_decoded_application_secret, string_to_sign.encode(), hashlib.sha256).digest() ).decode() From d52e838e6d2aa6fc665c181590f05ce0b5484742 Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 18 Jan 2024 15:54:30 +0100 Subject: [PATCH 31/47] fix(Singature): verification timestamp added to the constructor --- sinch/core/signature.py | 11 +++++++++-- tests/integration/test_request_signing.py | 9 ++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/sinch/core/signature.py b/sinch/core/signature.py index 4028543..3cecc2b 100644 --- a/sinch/core/signature.py +++ b/sinch/core/signature.py @@ -5,12 +5,19 @@ class Signature: - def __init__(self, sinch, http_method, request_data, request_uri): + def __init__( + self, + sinch, + http_method, + request_data, + request_uri, + signature_timestamp=None + ): self.sinch = sinch self.http_method = http_method self.content_type = 'application/json; charset=UTF-8' self.request_data = request_data - self.signature_timestamp = datetime.now(timezone.utc).isoformat() + self.signature_timestamp = signature_timestamp or datetime.now(timezone.utc).isoformat() self.request_uri = request_uri self.authorization_signature = None diff --git a/tests/integration/test_request_signing.py b/tests/integration/test_request_signing.py index 65018ad..107ab34 100644 --- a/tests/integration/test_request_signing.py +++ b/tests/integration/test_request_signing.py @@ -11,7 +11,8 @@ def test_request_signature( sinch_client_sync, http_method="GET", request_data=json.dumps({"test": "test"}), - request_uri="/verification/v1/verifications" + request_uri="/verification/v1/verifications", + signature_timestamp=verification_request_signature_timestamp ) signature.signature_timestamp = verification_request_signature_timestamp signature.calculate() @@ -30,7 +31,8 @@ def test_request_signature_using_empty_body( sinch_client_sync, http_method="POST", request_data=None, - request_uri="/verification/v1/verifications" + request_uri="/verification/v1/verifications", + signature_timestamp=verification_request_signature_timestamp ) signature.signature_timestamp = verification_request_signature_timestamp signature.calculate() @@ -49,7 +51,8 @@ def test_get_headers_with_signature_and_async_client( sinch_client_async, http_method="POST", request_data=None, - request_uri="/verification/v1/verifications" + request_uri="/verification/v1/verifications", + signature_timestamp=verification_request_signature_timestamp ) signature.signature_timestamp = verification_request_signature_timestamp headers = signature.get_http_headers_with_signature() From 3c2a9ccac7e9fa1ebca228dbe9cad6d0394e26e9 Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 18 Jan 2024 16:06:23 +0100 Subject: [PATCH 32/47] feat(Sinature): content type added to the init --- sinch/core/signature.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sinch/core/signature.py b/sinch/core/signature.py index 3cecc2b..5e45626 100644 --- a/sinch/core/signature.py +++ b/sinch/core/signature.py @@ -11,11 +11,12 @@ def __init__( http_method, request_data, request_uri, + content_type=None, signature_timestamp=None ): self.sinch = sinch self.http_method = http_method - self.content_type = 'application/json; charset=UTF-8' + self.content_type = content_type or 'application/json; charset=UTF-8' self.request_data = request_data self.signature_timestamp = signature_timestamp or datetime.now(timezone.utc).isoformat() self.request_uri = request_uri From 2de175f66e05923f0a0dd84c9468496cd97fb9b7 Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 18 Jan 2024 18:55:29 +0100 Subject: [PATCH 33/47] fix(Verification): better naming --- sinch/domains/verification/__init__.py | 60 +++++++++---------- .../endpoints/get_verification_by_id.py | 12 ++-- .../endpoints/get_verification_by_identity.py | 12 ++-- .../get_verification_by_reference.py | 12 ++-- .../endpoints/report_verification_using_id.py | 12 ++-- .../report_verification_using_identity.py | 12 ++-- sinch/domains/verification/models/requests.py | 10 ++-- .../domains/verification/models/responses.py | 10 ++-- .../verification/test_get_report_using_id.py | 6 +- .../test_get_report_using_identity.py | 6 +- .../test_get_report_using_reference.py | 6 +- .../test_report_verification_using_id.py | 6 +- ...test_report_verification_using_identity.py | 6 +- 13 files changed, 85 insertions(+), 85 deletions(-) diff --git a/sinch/domains/verification/__init__.py b/sinch/domains/verification/__init__.py index 8a6ed6b..e32a8b2 100644 --- a/sinch/domains/verification/__init__.py +++ b/sinch/domains/verification/__init__.py @@ -1,34 +1,34 @@ from sinch.domains.verification.endpoints.start_verification import StartVerificationEndpoint from sinch.domains.verification.endpoints.report_verification_using_identity import ( - ReportVerificationUsingIdentityEndpoint + ReportVerificationByIdentityEndpoint ) from sinch.domains.verification.endpoints.report_verification_using_id import ( - ReportVerificationUsingIdEndpoint + ReportVerificationByIdEndpoint ) from sinch.domains.verification.endpoints.get_verification_by_id import ( - GetVerificationByIdEndpoint + GetVerificationStatusByIdEndpoint ) from sinch.domains.verification.endpoints.get_verification_by_identity import ( - GetVerificationByIdentityEndpoint + GetVerificationStatusByIdentityEndpoint ) from sinch.domains.verification.endpoints.get_verification_by_reference import ( - GetVerificationByReferenceEndpoint + GetVerificationStatusByReferenceEndpoint ) from sinch.domains.verification.models.responses import ( StartVerificationResponse, - ReportVerificationUsingIdentityResponse, - ReportVerificationUsingIdResponse, - GetVerificationByIdentityResponse, - GetVerificationByIdResponse, - GetVerificationByReferenceResponse + ReportVerificationByIdentityResponse, + ReportVerificationByIdResponse, + GetVerificationStatusByIdentityResponse, + GetVerificationStatusByIdResponse, + GetVerificationStatusByReferenceResponse ) from sinch.domains.verification.models.requests import ( StartVerificationRequest, - ReportVerificationUsingIdentityRequest, - ReportVerificationUsingIdRequest, - GetVerificationByIdentityRequest, - GetVerificationByIdRequest, - GetVerificationByReferenceRequest + ReportVerificationByIdentityRequest, + ReportVerificationByIdRequest, + GetVerificationStatusByIdRequest, + GetVerificationStatusByIdentityRequest, + GetVerificationStatusByReferenceRequest ) from sinch.domains.verification.enums import VerificationMethod @@ -62,10 +62,10 @@ def report_by_id( self, id: str, verification_report_request: dict - ) -> ReportVerificationUsingIdResponse: + ) -> ReportVerificationByIdResponse: return self._sinch.configuration.transport.request( - ReportVerificationUsingIdEndpoint( - request_data=ReportVerificationUsingIdRequest( + ReportVerificationByIdEndpoint( + request_data=ReportVerificationByIdRequest( id, verification_report_request ) @@ -76,10 +76,10 @@ def report_by_identity( self, endpoint, verification_report_request - ) -> ReportVerificationUsingIdentityResponse: + ) -> ReportVerificationByIdentityResponse: return self._sinch.configuration.transport.request( - ReportVerificationUsingIdentityEndpoint( - request_data=ReportVerificationUsingIdentityRequest( + ReportVerificationByIdentityEndpoint( + request_data=ReportVerificationByIdentityRequest( endpoint, verification_report_request ) @@ -91,28 +91,28 @@ class VerificationStatus: def __init__(self, sinch): self._sinch = sinch - def get_by_reference(self, reference) -> GetVerificationByReferenceResponse: + def get_by_reference(self, reference) -> GetVerificationStatusByReferenceResponse: return self._sinch.configuration.transport.request( - GetVerificationByReferenceEndpoint( - request_data=GetVerificationByReferenceRequest( + GetVerificationStatusByReferenceEndpoint( + request_data=GetVerificationStatusByReferenceRequest( reference=reference ) ) ) - def get_by_id(self, id) -> GetVerificationByIdResponse: + def get_by_id(self, id) -> GetVerificationStatusByIdResponse: return self._sinch.configuration.transport.request( - GetVerificationByIdEndpoint( - request_data=GetVerificationByIdRequest( + GetVerificationStatusByIdEndpoint( + request_data=GetVerificationStatusByIdRequest( id=id ) ) ) - def get_by_identity(self, endpoint, method) -> GetVerificationByIdentityResponse: + def get_by_identity(self, endpoint, method) -> GetVerificationStatusByIdentityResponse: return self._sinch.configuration.transport.request( - GetVerificationByIdentityEndpoint( - request_data=GetVerificationByIdentityRequest( + GetVerificationStatusByIdentityEndpoint( + request_data=GetVerificationStatusByIdentityRequest( endpoint=endpoint, method=method ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_id.py b/sinch/domains/verification/endpoints/get_verification_by_id.py index 4a4bee6..4dd2a2c 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_id.py +++ b/sinch/domains/verification/endpoints/get_verification_by_id.py @@ -1,16 +1,16 @@ from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods -from sinch.domains.verification.models.requests import GetVerificationByIdRequest -from sinch.domains.verification.models.responses import GetVerificationByIdResponse +from sinch.domains.verification.models.requests import GetVerificationStatusByIdRequest +from sinch.domains.verification.models.responses import GetVerificationStatusByIdResponse -class GetVerificationByIdEndpoint(VerificationEndpoint): +class GetVerificationStatusByIdEndpoint(VerificationEndpoint): ENDPOINT_URL = "{origin}/verification/v1/verifications/id/{id}" HTTP_METHOD = HTTPMethods.GET.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value - def __init__(self, request_data: GetVerificationByIdRequest): + def __init__(self, request_data: GetVerificationStatusByIdRequest): self.request_data = request_data def build_url(self, sinch): @@ -19,8 +19,8 @@ def build_url(self, sinch): id=self.request_data.id ) - def handle_response(self, response: HTTPResponse) -> GetVerificationByIdResponse: + def handle_response(self, response: HTTPResponse) -> GetVerificationStatusByIdResponse: super().handle_response(response) - return GetVerificationByIdResponse( + return GetVerificationStatusByIdResponse( **response.body ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_identity.py b/sinch/domains/verification/endpoints/get_verification_by_identity.py index 0f3c7d7..3612efb 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_identity.py +++ b/sinch/domains/verification/endpoints/get_verification_by_identity.py @@ -1,16 +1,16 @@ from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods -from sinch.domains.verification.models.requests import GetVerificationByIdentityRequest -from sinch.domains.verification.models.responses import GetVerificationByIdentityResponse +from sinch.domains.verification.models.requests import GetVerificationStatusByIdentityRequest +from sinch.domains.verification.models.responses import GetVerificationStatusByIdentityResponse -class GetVerificationByIdentityEndpoint(VerificationEndpoint): +class GetVerificationStatusByIdentityEndpoint(VerificationEndpoint): ENDPOINT_URL = "{origin}/verification/v1/verifications/{method}/number/{endpoint}" HTTP_METHOD = HTTPMethods.GET.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value - def __init__(self, request_data: GetVerificationByIdentityRequest): + def __init__(self, request_data: GetVerificationStatusByIdentityRequest): self.request_data = request_data def build_url(self, sinch): @@ -20,8 +20,8 @@ def build_url(self, sinch): endpoint=self.request_data.endpoint ) - def handle_response(self, response: HTTPResponse) -> GetVerificationByIdentityResponse: + def handle_response(self, response: HTTPResponse) -> GetVerificationStatusByIdentityResponse: super().handle_response(response) - return GetVerificationByIdentityResponse( + return GetVerificationStatusByIdentityResponse( **response.body ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_reference.py b/sinch/domains/verification/endpoints/get_verification_by_reference.py index d5796ca..944b417 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_reference.py +++ b/sinch/domains/verification/endpoints/get_verification_by_reference.py @@ -1,16 +1,16 @@ from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods -from sinch.domains.verification.models.requests import GetVerificationByReferenceRequest -from sinch.domains.verification.models.responses import GetVerificationByReferenceResponse +from sinch.domains.verification.models.requests import GetVerificationStatusByReferenceRequest +from sinch.domains.verification.models.responses import GetVerificationStatusByReferenceResponse -class GetVerificationByReferenceEndpoint(VerificationEndpoint): +class GetVerificationStatusByReferenceEndpoint(VerificationEndpoint): ENDPOINT_URL = "{origin}/verification/v1/verifications/reference/{reference}" HTTP_METHOD = HTTPMethods.GET.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value - def __init__(self, request_data: GetVerificationByReferenceRequest): + def __init__(self, request_data: GetVerificationStatusByReferenceRequest): self.request_data = request_data def build_url(self, sinch): @@ -19,8 +19,8 @@ def build_url(self, sinch): reference=self.request_data.reference ) - def handle_response(self, response: HTTPResponse) -> GetVerificationByReferenceResponse: + def handle_response(self, response: HTTPResponse) -> GetVerificationStatusByReferenceResponse: super().handle_response(response) - return GetVerificationByReferenceResponse( + return GetVerificationStatusByReferenceResponse( **response.body ) diff --git a/sinch/domains/verification/endpoints/report_verification_using_id.py b/sinch/domains/verification/endpoints/report_verification_using_id.py index d1c2e55..88043b6 100644 --- a/sinch/domains/verification/endpoints/report_verification_using_id.py +++ b/sinch/domains/verification/endpoints/report_verification_using_id.py @@ -2,16 +2,16 @@ from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods -from sinch.domains.verification.models.requests import ReportVerificationUsingIdRequest -from sinch.domains.verification.models.responses import ReportVerificationUsingIdResponse +from sinch.domains.verification.models.requests import ReportVerificationByIdRequest +from sinch.domains.verification.models.responses import ReportVerificationByIdResponse -class ReportVerificationUsingIdEndpoint(VerificationEndpoint): +class ReportVerificationByIdEndpoint(VerificationEndpoint): ENDPOINT_URL = "{origin}/verification/v1/verifications/id/{id}" HTTP_METHOD = HTTPMethods.PUT.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value - def __init__(self, request_data: ReportVerificationUsingIdRequest): + def __init__(self, request_data: ReportVerificationByIdRequest): self.request_data = request_data def build_url(self, sinch): @@ -23,8 +23,8 @@ def build_url(self, sinch): def request_body(self): return json.dumps(self.request_data.verification_report_request) - def handle_response(self, response: HTTPResponse) -> ReportVerificationUsingIdResponse: + def handle_response(self, response: HTTPResponse) -> ReportVerificationByIdResponse: super().handle_response(response) - return ReportVerificationUsingIdResponse( + return ReportVerificationByIdResponse( **response.body ) diff --git a/sinch/domains/verification/endpoints/report_verification_using_identity.py b/sinch/domains/verification/endpoints/report_verification_using_identity.py index f2d3638..6ddd069 100644 --- a/sinch/domains/verification/endpoints/report_verification_using_identity.py +++ b/sinch/domains/verification/endpoints/report_verification_using_identity.py @@ -2,16 +2,16 @@ from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods -from sinch.domains.verification.models.requests import ReportVerificationUsingIdentityRequest -from sinch.domains.verification.models.responses import ReportVerificationUsingIdentityResponse +from sinch.domains.verification.models.requests import ReportVerificationByIdentityRequest +from sinch.domains.verification.models.responses import ReportVerificationByIdentityResponse -class ReportVerificationUsingIdentityEndpoint(VerificationEndpoint): +class ReportVerificationByIdentityEndpoint(VerificationEndpoint): ENDPOINT_URL = "{origin}/verification/v1/verifications/number/{endpoint}" HTTP_METHOD = HTTPMethods.PUT.value HTTP_AUTHENTICATION = HTTPAuthentication.SIGNED.value - def __init__(self, request_data: ReportVerificationUsingIdentityRequest): + def __init__(self, request_data: ReportVerificationByIdentityRequest): self.request_data = request_data def build_url(self, sinch): @@ -23,8 +23,8 @@ def build_url(self, sinch): def request_body(self): return json.dumps(self.request_data.verification_report_request) - def handle_response(self, response: HTTPResponse) -> ReportVerificationUsingIdentityResponse: + def handle_response(self, response: HTTPResponse) -> ReportVerificationByIdentityResponse: super().handle_response(response) - return ReportVerificationUsingIdentityResponse( + return ReportVerificationByIdentityResponse( **response.body ) diff --git a/sinch/domains/verification/models/requests.py b/sinch/domains/verification/models/requests.py index f13120a..91e15ad 100644 --- a/sinch/domains/verification/models/requests.py +++ b/sinch/domains/verification/models/requests.py @@ -13,28 +13,28 @@ class StartVerificationRequest(SinchRequestBaseModel): @dataclass -class ReportVerificationUsingIdentityRequest(SinchRequestBaseModel): +class ReportVerificationByIdentityRequest(SinchRequestBaseModel): endpoint: str verification_report_request: dict @dataclass -class ReportVerificationUsingIdRequest(SinchRequestBaseModel): +class ReportVerificationByIdRequest(SinchRequestBaseModel): id: str verification_report_request: dict @dataclass -class GetVerificationByReferenceRequest(SinchRequestBaseModel): +class GetVerificationStatusByReferenceRequest(SinchRequestBaseModel): reference: str @dataclass -class GetVerificationByIdRequest(SinchRequestBaseModel): +class GetVerificationStatusByIdRequest(SinchRequestBaseModel): id: str @dataclass -class GetVerificationByIdentityRequest(SinchRequestBaseModel): +class GetVerificationStatusByIdentityRequest(SinchRequestBaseModel): endpoint: str method: str diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 83baf48..982daf3 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -31,25 +31,25 @@ class ReportVerificationResponse(SinchBaseModel): @dataclass -class ReportVerificationUsingIdentityResponse(ReportVerificationResponse): +class ReportVerificationByIdentityResponse(ReportVerificationResponse): pass @dataclass -class ReportVerificationUsingIdResponse(ReportVerificationResponse): +class ReportVerificationByIdResponse(ReportVerificationResponse): pass @dataclass -class GetVerificationByReferenceResponse(VerificationResponse): +class GetVerificationStatusByReferenceResponse(VerificationResponse): pass @dataclass -class GetVerificationByIdResponse(VerificationResponse): +class GetVerificationStatusByIdResponse(VerificationResponse): pass @dataclass -class GetVerificationByIdentityResponse(VerificationResponse): +class GetVerificationStatusByIdentityResponse(VerificationResponse): pass diff --git a/tests/e2e/verification/test_get_report_using_id.py b/tests/e2e/verification/test_get_report_using_id.py index 9058aef..6896290 100644 --- a/tests/e2e/verification/test_get_report_using_id.py +++ b/tests/e2e/verification/test_get_report_using_id.py @@ -1,4 +1,4 @@ -from sinch.domains.verification.models.responses import GetVerificationByIdResponse +from sinch.domains.verification.models.responses import GetVerificationStatusByIdResponse def test_get_report_verification_using_id( @@ -9,7 +9,7 @@ def test_get_report_verification_using_id( verification_response = sinch_client_sync.verification.verification_status.get_by_id( id=verification_id ) - assert isinstance(verification_response, GetVerificationByIdResponse) + assert isinstance(verification_response, GetVerificationStatusByIdResponse) async def test_get_report_verification_using_id_async( @@ -20,4 +20,4 @@ async def test_get_report_verification_using_id_async( verification_response = await sinch_client_async.verification.verification_status.get_by_id( id=verification_id ) - assert isinstance(verification_response, GetVerificationByIdResponse) + assert isinstance(verification_response, GetVerificationStatusByIdResponse) diff --git a/tests/e2e/verification/test_get_report_using_identity.py b/tests/e2e/verification/test_get_report_using_identity.py index 3e197bf..569369f 100644 --- a/tests/e2e/verification/test_get_report_using_identity.py +++ b/tests/e2e/verification/test_get_report_using_identity.py @@ -1,4 +1,4 @@ -from sinch.domains.verification.models.responses import GetVerificationByIdentityResponse +from sinch.domains.verification.models.responses import GetVerificationStatusByIdentityResponse def test_get_report_verification_using_identity( @@ -9,7 +9,7 @@ def test_get_report_verification_using_identity( endpoint=phone_number, method="sms" ) - assert isinstance(verification_response, GetVerificationByIdentityResponse) + assert isinstance(verification_response, GetVerificationStatusByIdentityResponse) async def test_get_report_verification_using_identity_async( @@ -20,4 +20,4 @@ async def test_get_report_verification_using_identity_async( endpoint=phone_number, method="sms" ) - assert isinstance(verification_response, GetVerificationByIdentityResponse) + assert isinstance(verification_response, GetVerificationStatusByIdentityResponse) diff --git a/tests/e2e/verification/test_get_report_using_reference.py b/tests/e2e/verification/test_get_report_using_reference.py index f368b35..b2443f1 100644 --- a/tests/e2e/verification/test_get_report_using_reference.py +++ b/tests/e2e/verification/test_get_report_using_reference.py @@ -1,4 +1,4 @@ -from sinch.domains.verification.models.responses import GetVerificationByReferenceResponse +from sinch.domains.verification.models.responses import GetVerificationStatusByReferenceResponse def test_get_report_verification_using_reference( @@ -7,7 +7,7 @@ def test_get_report_verification_using_reference( verification_response = sinch_client_sync.verification.verification_status.get_by_reference( reference="random" ) - assert isinstance(verification_response, GetVerificationByReferenceResponse) + assert isinstance(verification_response, GetVerificationStatusByReferenceResponse) async def test_get_report_verification_using_reference_async( @@ -16,4 +16,4 @@ async def test_get_report_verification_using_reference_async( verification_response = await sinch_client_async.verification.verification_status.get_by_reference( reference="random" ) - assert isinstance(verification_response, GetVerificationByReferenceResponse) + assert isinstance(verification_response, GetVerificationStatusByReferenceResponse) diff --git a/tests/e2e/verification/test_report_verification_using_id.py b/tests/e2e/verification/test_report_verification_using_id.py index f197e2e..67381db 100644 --- a/tests/e2e/verification/test_report_verification_using_id.py +++ b/tests/e2e/verification/test_report_verification_using_id.py @@ -1,4 +1,4 @@ -from sinch.domains.verification.models.responses import ReportVerificationUsingIdResponse +from sinch.domains.verification.models.responses import ReportVerificationByIdResponse def test_report_verification_using_id_and_sms( @@ -15,7 +15,7 @@ def test_report_verification_using_id_and_sms( } } ) - assert isinstance(verification_response, ReportVerificationUsingIdResponse) + assert isinstance(verification_response, ReportVerificationByIdResponse) async def test_report_verification_using_id_and_sms_async( @@ -32,4 +32,4 @@ async def test_report_verification_using_id_and_sms_async( } } ) - assert isinstance(verification_response, ReportVerificationUsingIdResponse) + assert isinstance(verification_response, ReportVerificationByIdResponse) diff --git a/tests/e2e/verification/test_report_verification_using_identity.py b/tests/e2e/verification/test_report_verification_using_identity.py index 6071d2f..816c6a6 100644 --- a/tests/e2e/verification/test_report_verification_using_identity.py +++ b/tests/e2e/verification/test_report_verification_using_identity.py @@ -1,4 +1,4 @@ -from sinch.domains.verification.models.responses import ReportVerificationUsingIdentityResponse +from sinch.domains.verification.models.responses import ReportVerificationByIdentityResponse def test_report_verification_using_identity_and_sms( @@ -14,7 +14,7 @@ def test_report_verification_using_identity_and_sms( } } ) - assert isinstance(verification_response, ReportVerificationUsingIdentityResponse) + assert isinstance(verification_response, ReportVerificationByIdentityResponse) async def test_report_verification_using_identity_and_sms_async( @@ -30,4 +30,4 @@ async def test_report_verification_using_identity_and_sms_async( } } ) - assert isinstance(verification_response, ReportVerificationUsingIdentityResponse) + assert isinstance(verification_response, ReportVerificationByIdentityResponse) From a1fb0ca1c67c100af3fef76943910355e1fc4500 Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 18 Jan 2024 19:13:14 +0100 Subject: [PATCH 34/47] fix(Verification): enum for method added --- sinch/domains/verification/models/requests.py | 2 +- sinch/domains/verification/models/responses.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sinch/domains/verification/models/requests.py b/sinch/domains/verification/models/requests.py index 91e15ad..fddb010 100644 --- a/sinch/domains/verification/models/requests.py +++ b/sinch/domains/verification/models/requests.py @@ -37,4 +37,4 @@ class GetVerificationStatusByIdRequest(SinchRequestBaseModel): @dataclass class GetVerificationStatusByIdentityRequest(SinchRequestBaseModel): endpoint: str - method: str + method: VerificationMethod diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 982daf3..22708fb 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -1,11 +1,12 @@ from dataclasses import dataclass from sinch.core.models.base_model import SinchBaseModel +from sinch.domains.verification.enums import VerificationMethod @dataclass class StartVerificationResponse(SinchBaseModel): id: str - method: str + method: VerificationMethod sms: dict _links: list @@ -13,7 +14,7 @@ class StartVerificationResponse(SinchBaseModel): @dataclass class VerificationResponse(SinchBaseModel): id: str - method: str + method: VerificationMethod status: str price: dict identity: str @@ -26,7 +27,7 @@ class VerificationResponse(SinchBaseModel): class ReportVerificationResponse(SinchBaseModel): id: str reference: str - method: str + method: VerificationMethod status: str From 86174a81a8d906605aece1381aa61debc3c5629d Mon Sep 17 00:00:00 2001 From: 650elx Date: Thu, 18 Jan 2024 19:26:39 +0100 Subject: [PATCH 35/47] test(Verification): redunant lines removed --- tests/integration/test_request_signing.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/integration/test_request_signing.py b/tests/integration/test_request_signing.py index 107ab34..46e49ab 100644 --- a/tests/integration/test_request_signing.py +++ b/tests/integration/test_request_signing.py @@ -14,7 +14,6 @@ def test_request_signature( request_uri="/verification/v1/verifications", signature_timestamp=verification_request_signature_timestamp ) - signature.signature_timestamp = verification_request_signature_timestamp signature.calculate() assert signature.authorization_signature @@ -34,7 +33,6 @@ def test_request_signature_using_empty_body( request_uri="/verification/v1/verifications", signature_timestamp=verification_request_signature_timestamp ) - signature.signature_timestamp = verification_request_signature_timestamp signature.calculate() assert signature.authorization_signature @@ -54,7 +52,6 @@ def test_get_headers_with_signature_and_async_client( request_uri="/verification/v1/verifications", signature_timestamp=verification_request_signature_timestamp ) - signature.signature_timestamp = verification_request_signature_timestamp headers = signature.get_http_headers_with_signature() assert "x-timestamp" in headers From edc92d4a2b63cda99c3a505f907ef4aa25618c66 Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 19 Jan 2024 12:12:04 +0100 Subject: [PATCH 36/47] feat(CI): Verification API specific env vars added --- .github/workflows/run-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 0bcf118..45d9ff7 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -25,6 +25,9 @@ env: APPLICATION_KEY: ${{ secrets.APPLICATION_KEY }} VERIFICATION_ID: ${{ secrets.VERIFICATION_ID }} VERIFICATION_ORIGIN: ${{ secrets.VERIFICATION_ORIGIN}} + VERIFICATION_REQUEST_SIGNATURE_TIMESTAMP: ${{ secrets.VERIFICATION_REQUEST_SIGNATURE_TIMESTAMP}} + VERIFICATION_REQUEST_WITH_EMPTY_BODY_SIGNATURE: ${{ secrets.VERIFICATION_REQUEST_WITH_EMPTY_BODY_SIGNATURE}} + VERIFICATION_REQUEST_SIGNATURE: ${{ secrets.VERIFICATION_REQUEST_SIGNATURE}} jobs: build: From bd32d50e139a30235af2b36181d277ada7847ed5 Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 19 Jan 2024 17:15:14 +0100 Subject: [PATCH 37/47] test(Logging): caplog usage fixed --- tests/integration/test_logging.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_logging.py b/tests/integration/test_logging.py index c551416..fca24f6 100644 --- a/tests/integration/test_logging.py +++ b/tests/integration/test_logging.py @@ -17,7 +17,7 @@ def test_default_logger(sinch_client_sync, caplog): sinch_client = mock_http_transport(sinch_client_sync) http_endpoint = Mock() sinch_client.configuration.transport.request(http_endpoint) - assert len(caplog.records) == 2 + assert len(caplog.get_records("call")) == 2 assert caplog.records[0].levelname == "DEBUG" @@ -27,7 +27,7 @@ def test_changing_logger_name_within_the_client(sinch_client_sync, caplog): sinch_client = mock_http_transport(sinch_client_sync) http_endpoint = Mock() sinch_client.configuration.transport.request(http_endpoint) - assert len(caplog.records) == 2 + assert len(caplog.get_records("call")) == 2 assert caplog.records[0].name == "SumOlimpijczyk" From 2a80584416f8cbab8dc50556093f92e3d6fac95f Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 19 Jan 2024 18:26:36 +0100 Subject: [PATCH 38/47] test(Logging): simpfly caplog usage --- tests/integration/test_logging.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/integration/test_logging.py b/tests/integration/test_logging.py index fca24f6..e8543b8 100644 --- a/tests/integration/test_logging.py +++ b/tests/integration/test_logging.py @@ -22,13 +22,16 @@ def test_default_logger(sinch_client_sync, caplog): def test_changing_logger_name_within_the_client(sinch_client_sync, caplog): + logger_name = "SumOlimpijczyk" sinch_client_sync.configuration.logger.setLevel(logging.DEBUG) - sinch_client_sync.configuration.logger.name = "SumOlimpijczyk" + sinch_client_sync.configuration.logger.name = logger_name sinch_client = mock_http_transport(sinch_client_sync) + caplog.set_level(logging.DEBUG, logger=logger_name) http_endpoint = Mock() sinch_client.configuration.transport.request(http_endpoint) - assert len(caplog.get_records("call")) == 2 - assert caplog.records[0].name == "SumOlimpijczyk" + + assert len(caplog.records) == 2 + assert caplog.records[0].name == logger_name def test_logger_with_logging_to_file(sinch_client_sync): From 79da66786ae526544c4df36e44785a41897155e9 Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 19 Jan 2024 19:31:22 +0100 Subject: [PATCH 39/47] test(Logging): filter for only relevant log records --- tests/integration/test_logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_logging.py b/tests/integration/test_logging.py index e8543b8..0e62482 100644 --- a/tests/integration/test_logging.py +++ b/tests/integration/test_logging.py @@ -30,7 +30,7 @@ def test_changing_logger_name_within_the_client(sinch_client_sync, caplog): http_endpoint = Mock() sinch_client.configuration.transport.request(http_endpoint) - assert len(caplog.records) == 2 + assert len([record for record in caplog.records if record.name == logger_name]) == 2 assert caplog.records[0].name == logger_name From 3b1c76519c5889ef6ee425ffe89a3bd8bf0af40c Mon Sep 17 00:00:00 2001 From: 650elx Date: Mon, 22 Jan 2024 09:38:07 +0100 Subject: [PATCH 40/47] feat(Verfication): DTO improved --- .../endpoints/get_verification_by_id.py | 11 +++++- .../endpoints/get_verification_by_identity.py | 11 +++++- .../get_verification_by_reference.py | 11 +++++- .../endpoints/start_verification.py | 8 ++++- sinch/domains/verification/enums.py | 9 +++++ .../domains/verification/models/responses.py | 16 ++++++--- tests/conftest.py | 36 +++++-------------- .../verification/test_start_verification.py | 18 +++++++++- 8 files changed, 82 insertions(+), 38 deletions(-) diff --git a/sinch/domains/verification/endpoints/get_verification_by_id.py b/sinch/domains/verification/endpoints/get_verification_by_id.py index 4dd2a2c..131e1e7 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_id.py +++ b/sinch/domains/verification/endpoints/get_verification_by_id.py @@ -22,5 +22,14 @@ def build_url(self, sinch): def handle_response(self, response: HTTPResponse) -> GetVerificationStatusByIdResponse: super().handle_response(response) return GetVerificationStatusByIdResponse( - **response.body + id=response.body.get("id"), + method=response.body.get("method"), + status=response.body.get("status"), + price=response.body.get("price"), + identity=response.body.get("identity"), + country_id=response.body.get("country_id"), + verification_timestamp=response.body.get("verification_timestamp"), + reference=response.body.get("reference"), + reason=response.body.get("reason"), + call_complete=response.body.get("call_complete") ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_identity.py b/sinch/domains/verification/endpoints/get_verification_by_identity.py index 3612efb..88008df 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_identity.py +++ b/sinch/domains/verification/endpoints/get_verification_by_identity.py @@ -23,5 +23,14 @@ def build_url(self, sinch): def handle_response(self, response: HTTPResponse) -> GetVerificationStatusByIdentityResponse: super().handle_response(response) return GetVerificationStatusByIdentityResponse( - **response.body + id=response.body.get("id"), + method=response.body.get("method"), + status=response.body.get("status"), + price=response.body.get("price"), + identity=response.body.get("identity"), + country_id=response.body.get("country_id"), + verification_timestamp=response.body.get("verification_timestamp"), + reference=response.body.get("reference"), + reason=response.body.get("reason"), + call_complete=response.body.get("call_complete") ) diff --git a/sinch/domains/verification/endpoints/get_verification_by_reference.py b/sinch/domains/verification/endpoints/get_verification_by_reference.py index 944b417..3a5115b 100644 --- a/sinch/domains/verification/endpoints/get_verification_by_reference.py +++ b/sinch/domains/verification/endpoints/get_verification_by_reference.py @@ -22,5 +22,14 @@ def build_url(self, sinch): def handle_response(self, response: HTTPResponse) -> GetVerificationStatusByReferenceResponse: super().handle_response(response) return GetVerificationStatusByReferenceResponse( - **response.body + id=response.body.get("id"), + method=response.body.get("method"), + status=response.body.get("status"), + price=response.body.get("price"), + identity=response.body.get("identity"), + country_id=response.body.get("country_id"), + verification_timestamp=response.body.get("verification_timestamp"), + reference=response.body.get("reference"), + reason=response.body.get("reason"), + call_complete=response.body.get("call_complete") ) diff --git a/sinch/domains/verification/endpoints/start_verification.py b/sinch/domains/verification/endpoints/start_verification.py index 0508ff8..2770367 100644 --- a/sinch/domains/verification/endpoints/start_verification.py +++ b/sinch/domains/verification/endpoints/start_verification.py @@ -23,5 +23,11 @@ def request_body(self): def handle_response(self, response: HTTPResponse) -> StartVerificationResponse: return StartVerificationResponse( - **response.body + id=response.body.get("id"), + method=response.body.get("method"), + _links=response.body.get("_links"), + sms=response.body.get("sms"), + flash_call=response.body.get("flashCall"), + callout=response.body.get("callout"), + seamless=response.body.get("seamless"), ) diff --git a/sinch/domains/verification/enums.py b/sinch/domains/verification/enums.py index 38dc5f2..8ba9441 100644 --- a/sinch/domains/verification/enums.py +++ b/sinch/domains/verification/enums.py @@ -6,3 +6,12 @@ class VerificationMethod(Enum): FLASHCALL = "flashCall" CALLOUT = "callout" SEAMLESS = "seamless" + + +class VerificationStatus(Enum): + PENDING = "PENDING" + SUCCESSFUL = "SUCCESSFUL" + FAIL = "FAIL" + DENIED = "DENIED" + ABORTED = "ABORTED" + ERROR = "ERROR" diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 22708fb..a77289c 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -1,26 +1,32 @@ from dataclasses import dataclass from sinch.core.models.base_model import SinchBaseModel -from sinch.domains.verification.enums import VerificationMethod +from sinch.domains.verification.enums import VerificationMethod, VerificationStatus +from typing import Optional @dataclass class StartVerificationResponse(SinchBaseModel): id: str method: VerificationMethod - sms: dict _links: list + sms: Optional[dict] = None + flash_call: Optional[dict] = None + callout: Optional[dict] = None + seamless: Optional[dict] = None @dataclass class VerificationResponse(SinchBaseModel): id: str method: VerificationMethod - status: str + status: VerificationStatus price: dict identity: str - countryId: str - verificationTimestamp: str + country_id: str + verification_timestamp: str reference: str + reason: str + call_complete: bool @dataclass diff --git a/tests/conftest.py b/tests/conftest.py index 6b84ada..a7cb74f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -276,34 +276,6 @@ def second_int_based_pagination_response(): ) -@pytest.fixture -def int_based_pagination_request_data(): - return IntBasedPaginationRequest( - page=0, - page_size=2 - ) - - -@pytest.fixture -def first_int_based_pagination_response(): - return IntBasedPaginationResponse( - count=4, - page=0, - page_size=2, - pig_dogs=["Bartosz", "Piotr"] - ) - - -@pytest.fixture -def second_int_based_pagination_response(): - return IntBasedPaginationResponse( - count=4, - page=1, - page_size=2, - pig_dogs=["Walaszek", "Połać"] - ) - - @pytest.fixture def third_int_based_pagination_response(): return IntBasedPaginationResponse( @@ -314,6 +286,14 @@ def third_int_based_pagination_response(): ) +@pytest.fixture +def int_based_pagination_request_data(): + return IntBasedPaginationRequest( + page=0, + page_size=2 + ) + + @pytest.fixture def sinch_client_sync( key_id, diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py index 2456f88..79169f1 100644 --- a/tests/e2e/verification/test_start_verification.py +++ b/tests/e2e/verification/test_start_verification.py @@ -1,7 +1,7 @@ from sinch.domains.verification.models.responses import StartVerificationResponse -def test_start_verification( +def test_start_verification_sms( sinch_client_sync, phone_number ): @@ -17,6 +17,22 @@ def test_start_verification( assert isinstance(verification_response, StartVerificationResponse) +def test_start_verification_flash_call( + sinch_client_sync, + phone_number +): + verification_response = sinch_client_sync.verification.verifications.start( + method="flashCall", + identity={ + "type": "number", + "endpoint": phone_number + }, + reference="random5" + ) + + assert isinstance(verification_response, StartVerificationResponse) + + async def test_start_verification_async( sinch_client_async, phone_number From 0ac9c0ae1bec6b28bf52775c3a0b16043071b450 Mon Sep 17 00:00:00 2001 From: 650elx Date: Mon, 22 Jan 2024 12:43:37 +0100 Subject: [PATCH 41/47] fix(Verification): simplyfy model usage --- .../endpoints/report_verification_using_id.py | 11 ++++++++++- .../endpoints/report_verification_using_identity.py | 11 ++++++++++- sinch/domains/verification/models/responses.py | 7 ++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/sinch/domains/verification/endpoints/report_verification_using_id.py b/sinch/domains/verification/endpoints/report_verification_using_id.py index 88043b6..63bbb9e 100644 --- a/sinch/domains/verification/endpoints/report_verification_using_id.py +++ b/sinch/domains/verification/endpoints/report_verification_using_id.py @@ -26,5 +26,14 @@ def request_body(self): def handle_response(self, response: HTTPResponse) -> ReportVerificationByIdResponse: super().handle_response(response) return ReportVerificationByIdResponse( - **response.body + id=response.body.get("id"), + method=response.body.get("method"), + status=response.body.get("status"), + price=response.body.get("price"), + identity=response.body.get("identity"), + country_id=response.body.get("country_id"), + verification_timestamp=response.body.get("verification_timestamp"), + reference=response.body.get("reference"), + reason=response.body.get("reason"), + call_complete=response.body.get("call_complete") ) diff --git a/sinch/domains/verification/endpoints/report_verification_using_identity.py b/sinch/domains/verification/endpoints/report_verification_using_identity.py index 6ddd069..dbc4287 100644 --- a/sinch/domains/verification/endpoints/report_verification_using_identity.py +++ b/sinch/domains/verification/endpoints/report_verification_using_identity.py @@ -26,5 +26,14 @@ def request_body(self): def handle_response(self, response: HTTPResponse) -> ReportVerificationByIdentityResponse: super().handle_response(response) return ReportVerificationByIdentityResponse( - **response.body + id=response.body.get("id"), + method=response.body.get("method"), + status=response.body.get("status"), + price=response.body.get("price"), + identity=response.body.get("identity"), + country_id=response.body.get("country_id"), + verification_timestamp=response.body.get("verification_timestamp"), + reference=response.body.get("reference"), + reason=response.body.get("reason"), + call_complete=response.body.get("call_complete") ) diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index a77289c..0972ac4 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -30,11 +30,8 @@ class VerificationResponse(SinchBaseModel): @dataclass -class ReportVerificationResponse(SinchBaseModel): - id: str - reference: str - method: VerificationMethod - status: str +class ReportVerificationResponse(VerificationResponse): + pass @dataclass From 97d53f2cb816bb6579fe5900134fe438867380a7 Mon Sep 17 00:00:00 2001 From: 650elx Date: Mon, 22 Jan 2024 14:27:43 +0100 Subject: [PATCH 42/47] feat(Verification): Start verification splitted into smaller DTOs --- .../endpoints/start_verification.py | 37 ++++++++++++++----- .../domains/verification/models/responses.py | 25 ++++++++++--- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/sinch/domains/verification/endpoints/start_verification.py b/sinch/domains/verification/endpoints/start_verification.py index 2770367..db52c8c 100644 --- a/sinch/domains/verification/endpoints/start_verification.py +++ b/sinch/domains/verification/endpoints/start_verification.py @@ -1,8 +1,15 @@ from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods +from sinch.domains.verification.enums import VerificationMethod from sinch.domains.verification.models.requests import StartVerificationRequest -from sinch.domains.verification.models.responses import StartVerificationResponse +from sinch.domains.verification.models.responses import ( + StartVerificationResponse, + StartSMSInitiateVerificationResponse, + StartDataInitiateVerificationResponse, + StartCalloutInitiateVerificationResponse, + StartFlashCallInitiateVerificationResponse +) class StartVerificationEndpoint(VerificationEndpoint): @@ -22,12 +29,22 @@ def request_body(self): return self.request_data.as_json() def handle_response(self, response: HTTPResponse) -> StartVerificationResponse: - return StartVerificationResponse( - id=response.body.get("id"), - method=response.body.get("method"), - _links=response.body.get("_links"), - sms=response.body.get("sms"), - flash_call=response.body.get("flashCall"), - callout=response.body.get("callout"), - seamless=response.body.get("seamless"), - ) + if self.request_data.method == VerificationMethod.SMS.value: + return StartSMSInitiateVerificationResponse( + **response.body + ) + elif self.request_data.method == VerificationMethod.FLASHCALL.value: + return StartFlashCallInitiateVerificationResponse( + id=response.body.get("id"), + method=response.body.get("method"), + _links=response.body.get("_links"), + flash_call=response.body.get("flashCall") + ) + elif self.request_data.method == VerificationMethod.CALLOUT.value: + return StartCalloutInitiateVerificationResponse( + **response.body + ) + elif self.request_data.method == VerificationMethod.SEAMLESS.value: + return StartDataInitiateVerificationResponse( + **response.body + ) diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 0972ac4..de33390 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -1,7 +1,6 @@ from dataclasses import dataclass from sinch.core.models.base_model import SinchBaseModel from sinch.domains.verification.enums import VerificationMethod, VerificationStatus -from typing import Optional @dataclass @@ -9,10 +8,26 @@ class StartVerificationResponse(SinchBaseModel): id: str method: VerificationMethod _links: list - sms: Optional[dict] = None - flash_call: Optional[dict] = None - callout: Optional[dict] = None - seamless: Optional[dict] = None + + +@dataclass +class StartSMSInitiateVerificationResponse(StartVerificationResponse): + sms: dict + + +@dataclass +class StartFlashCallInitiateVerificationResponse(StartVerificationResponse): + flash_call: dict + + +@dataclass +class StartCalloutInitiateVerificationResponse(StartVerificationResponse): + callout: dict + + +@dataclass +class StartDataInitiateVerificationResponse(StartVerificationResponse): + seamless: dict @dataclass From f444e8443923132345f69b62035de9ffba18d6c7 Mon Sep 17 00:00:00 2001 From: 650elx Date: Mon, 22 Jan 2024 14:40:19 +0100 Subject: [PATCH 43/47] fix(Verification): identity type changed --- sinch/domains/verification/models/responses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index de33390..3384b79 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -36,7 +36,7 @@ class VerificationResponse(SinchBaseModel): method: VerificationMethod status: VerificationStatus price: dict - identity: str + identity: dict country_id: str verification_timestamp: str reference: str From 53691e104a9381a4095c53b20da30865e495b30e Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 26 Jan 2024 11:05:53 +0100 Subject: [PATCH 44/47] fix(Verification): flashcall naming unified --- .../domains/verification/endpoints/start_verification.py | 8 +++++++- sinch/domains/verification/enums.py | 2 +- sinch/domains/verification/models/responses.py | 2 +- tests/e2e/verification/test_start_verification.py | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sinch/domains/verification/endpoints/start_verification.py b/sinch/domains/verification/endpoints/start_verification.py index db52c8c..e02f1fd 100644 --- a/sinch/domains/verification/endpoints/start_verification.py +++ b/sinch/domains/verification/endpoints/start_verification.py @@ -1,3 +1,4 @@ +from copy import deepcopy from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods @@ -26,6 +27,11 @@ def build_url(self, sinch): ) def request_body(self): + if self.request_data.method == VerificationMethod.FLASHCALL.value: + request_data = deepcopy(self.request_data) + request_data.method = "flashCall" + return request_data.as_json() + return self.request_data.as_json() def handle_response(self, response: HTTPResponse) -> StartVerificationResponse: @@ -38,7 +44,7 @@ def handle_response(self, response: HTTPResponse) -> StartVerificationResponse: id=response.body.get("id"), method=response.body.get("method"), _links=response.body.get("_links"), - flash_call=response.body.get("flashCall") + flashcall=response.body.get("flashCall") ) elif self.request_data.method == VerificationMethod.CALLOUT.value: return StartCalloutInitiateVerificationResponse( diff --git a/sinch/domains/verification/enums.py b/sinch/domains/verification/enums.py index 8ba9441..82f6c80 100644 --- a/sinch/domains/verification/enums.py +++ b/sinch/domains/verification/enums.py @@ -3,7 +3,7 @@ class VerificationMethod(Enum): SMS = "sms" - FLASHCALL = "flashCall" + FLASHCALL = "flashcall" CALLOUT = "callout" SEAMLESS = "seamless" diff --git a/sinch/domains/verification/models/responses.py b/sinch/domains/verification/models/responses.py index 3384b79..911c119 100644 --- a/sinch/domains/verification/models/responses.py +++ b/sinch/domains/verification/models/responses.py @@ -17,7 +17,7 @@ class StartSMSInitiateVerificationResponse(StartVerificationResponse): @dataclass class StartFlashCallInitiateVerificationResponse(StartVerificationResponse): - flash_call: dict + flashcall: dict @dataclass diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py index 79169f1..ca38f30 100644 --- a/tests/e2e/verification/test_start_verification.py +++ b/tests/e2e/verification/test_start_verification.py @@ -1,4 +1,5 @@ from sinch.domains.verification.models.responses import StartVerificationResponse +from sinch.domains.verification.enums import VerificationMethod def test_start_verification_sms( @@ -22,7 +23,7 @@ def test_start_verification_flash_call( phone_number ): verification_response = sinch_client_sync.verification.verifications.start( - method="flashCall", + method=VerificationMethod.FLASHCALL.value, identity={ "type": "number", "endpoint": phone_number From cc6a49e462b210ec175aa74ff3b159e954b58aea Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 26 Jan 2024 13:14:24 +0100 Subject: [PATCH 45/47] fix(Verification): redundant transformation removed --- sinch/domains/verification/endpoints/start_verification.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sinch/domains/verification/endpoints/start_verification.py b/sinch/domains/verification/endpoints/start_verification.py index e02f1fd..bc4f5b5 100644 --- a/sinch/domains/verification/endpoints/start_verification.py +++ b/sinch/domains/verification/endpoints/start_verification.py @@ -27,11 +27,6 @@ def build_url(self, sinch): ) def request_body(self): - if self.request_data.method == VerificationMethod.FLASHCALL.value: - request_data = deepcopy(self.request_data) - request_data.method = "flashCall" - return request_data.as_json() - return self.request_data.as_json() def handle_response(self, response: HTTPResponse) -> StartVerificationResponse: From 13ce08f471077cc4edbb690e22e5084e7ac45648 Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 26 Jan 2024 14:38:02 +0100 Subject: [PATCH 46/47] test(Verification): more specific DTOs assertions --- tests/e2e/verification/test_start_verification.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/e2e/verification/test_start_verification.py b/tests/e2e/verification/test_start_verification.py index ca38f30..da3dfb2 100644 --- a/tests/e2e/verification/test_start_verification.py +++ b/tests/e2e/verification/test_start_verification.py @@ -1,4 +1,7 @@ -from sinch.domains.verification.models.responses import StartVerificationResponse +from sinch.domains.verification.models.responses import ( + StartSMSInitiateVerificationResponse, + StartFlashCallInitiateVerificationResponse +) from sinch.domains.verification.enums import VerificationMethod @@ -15,7 +18,7 @@ def test_start_verification_sms( reference="random" ) - assert isinstance(verification_response, StartVerificationResponse) + assert isinstance(verification_response, StartSMSInitiateVerificationResponse) def test_start_verification_flash_call( @@ -31,7 +34,7 @@ def test_start_verification_flash_call( reference="random5" ) - assert isinstance(verification_response, StartVerificationResponse) + assert isinstance(verification_response, StartFlashCallInitiateVerificationResponse) async def test_start_verification_async( @@ -47,4 +50,4 @@ async def test_start_verification_async( reference="random" ) - assert isinstance(verification_response, StartVerificationResponse) + assert isinstance(verification_response, StartSMSInitiateVerificationResponse) From 2d7a61e38b8e17cd1d1623a60a02d50160f4c4ad Mon Sep 17 00:00:00 2001 From: 650elx Date: Fri, 26 Jan 2024 14:43:38 +0100 Subject: [PATCH 47/47] fix(style): remove unused import --- sinch/domains/verification/endpoints/start_verification.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sinch/domains/verification/endpoints/start_verification.py b/sinch/domains/verification/endpoints/start_verification.py index bc4f5b5..5902203 100644 --- a/sinch/domains/verification/endpoints/start_verification.py +++ b/sinch/domains/verification/endpoints/start_verification.py @@ -1,4 +1,3 @@ -from copy import deepcopy from sinch.core.models.http_response import HTTPResponse from sinch.domains.verification.endpoints.verification_endpoint import VerificationEndpoint from sinch.core.enums import HTTPAuthentication, HTTPMethods