Skip to content

Commit

Permalink
preparing for alpha release
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkahan committed Feb 22, 2024
1 parent 7736da0 commit 715e9f6
Show file tree
Hide file tree
Showing 47 changed files with 244 additions and 80 deletions.
9 changes: 7 additions & 2 deletions http_client/BUILD
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
resource(name='pyproject', source='pyproject.toml')
file(name='readme', source='README.md')

files(sources=['tests/data/*'])

python_distribution(
name='vonage-http-client',
dependencies=[':pyproject', 'http_client/src/http_client', 'utils/src/utils'],
dependencies=[
':pyproject',
':readme',
'http_client/src/vonage_http_client:http_client',
],
provides=python_artifact(),
generate_setup=False,
repositories=['https://test.pypi.org/legacy/'],
repositories=['@pypi'],
)
55 changes: 49 additions & 6 deletions http_client/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,60 @@
# Need to redo this to be about the client!
# Vonage HTTP Client Package

Vonage Authentication Package
This Python package provides a synchronous HTTP client for sending authenticated requests to Vonage APIs.

`vonage-auth` provides a convenient way to handle authentication related to Vonage APIs in your Python projects. This package includes an `Auth` class that allows you to manage API key- and secret-based authentication as well as JSON Web Token (JWT) authentication.
This package (`vonage-http-client`) is used by the `vonage` Python package and SDK so doesn't require manual installation or config unless you're using this package independently of a SDK.

This package (`vonage-auth`) is used by the `vonage` Python package so doesn't require manual installation unless you're not using that.
The `HttpClient` class is initialized with an instance of the `Auth` class for credentials, an optional class of HTTP client options, and an optional SDK version (this is provided automatically when using this module via an SDK).

The `HttpClientOptions` class defines the options for the HTTP client, including the API and REST hosts, timeout, pool connections, pool max size, and max retries.

This package also includes an `Auth` class that allows you to manage API key- and secret-based authentication as well as JSON Web Token (JWT) authentication.

For full API documentation refer to the [Vonage Developer documentation](https://developer.vonage.com).

## Installation
## Installation (if not using via an SDK)

You can install the package using pip:

```bash
pip install vonage-auth
pip install vonage-http-client
```

## Usage

```python
from vonage_http_client import HttpClient, HttpClientOptions
from vonage_http_client.auth import Auth

# Create an Auth instance
auth = Auth(api_key='your_api_key', api_secret='your_api_secret')

# Create HttpClientOptions instance
options = HttpClientOptions(api_host='api.nexmo.com', timeout=30)

# Create a HttpClient instance
client = HttpClient(auth=auth, http_client_options=options)

# Make a GET request
response = client.get(host='api.nexmo.com', request_path='/v1/messages')

# Make a POST request
response = client.post(host='api.nexmo.com', request_path='/v1/messages', params={'key': 'value'})
```

### Appending to the User-Agent Header

The HttpClient class also supports appending additional information to the User-Agent header via the append_to_user_agent method:

```python
client.append_to_user_agent('additional_info')
```

### Changing the Authentication Method Used

The `HttpClient` class automatically handles JWT and basic authentication based on the Auth instance provided. It uses JWT authentication by default, but you can specify the authentication type when making a request:

```python
# Use basic authentication for this request
response = client.get(host='api.nexmo.com', request_path='/v1/messages', auth_type='basic')
```
12 changes: 6 additions & 6 deletions http_client/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[project]
name = 'vonage-http-client'
version = '0.1.0'
description = 'An HTTP client for making requests to Vonage APIs.'
name = "vonage-http-client"
version = "1.0.0"
description = "An HTTP client for making requests to Vonage APIs."
readme = "README.md"
authors = [{ name = "Vonage", email = "devrel@vonage.com" }]
requires-python = ">=3.8"
dependencies = [
"vonage-utils>=0.1.0",
"vonage-utils>=1.0.0",
"vonage-jwt>=1.1.0",
"requests==2.*",
"requests>=2.27.0",
"pydantic>=2.6.1",
"typing_extensions>=4.9.0",
]
Expand All @@ -24,7 +24,7 @@ classifiers = [
]

[project.urls]
homepage = "https://github.com/Vonage/vonage-python-sdk"
Homepage = "https://github.com/Vonage/vonage-python-sdk"

[build-system]
requires = ["setuptools>=61.0", "wheel"]
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from json import JSONDecodeError

from requests import Response
from utils.errors import VonageError
from vonage_utils.errors import VonageError


class JWTGenerationError(VonageError):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
from platform import python_version
from typing import Literal, Optional, Union

from http_client.auth import Auth
from http_client.errors import (
from pydantic import BaseModel, Field, ValidationError, validate_call
from requests import Response
from requests.adapters import HTTPAdapter
from requests.sessions import Session
from typing_extensions import Annotated
from vonage_http_client.auth import Auth
from vonage_http_client.errors import (
AuthenticationError,
HttpRequestError,
InvalidHttpClientOptionsError,
RateLimitedError,
ServerError,
)
from pydantic import BaseModel, Field, ValidationError, validate_call
from requests import Response
from requests.adapters import HTTPAdapter
from requests.sessions import Session
from typing_extensions import Annotated

logger = getLogger('vonage')

Expand Down
8 changes: 4 additions & 4 deletions http_client/tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from os.path import dirname, join
from unittest.mock import patch

from http_client.auth import Auth
from http_client.errors import InvalidAuthError, JWTGenerationError
from pydantic import ValidationError
from pytest import raises
from vonage_http_client.auth import Auth
from vonage_http_client.errors import InvalidAuthError, JWTGenerationError
from vonage_jwt.jwt import JwtClient


Expand Down Expand Up @@ -81,14 +81,14 @@ def vonage_jwt_mock(self):

def test_generate_application_jwt():
auth = Auth(application_id=application_id, private_key=private_key)
with patch('http_client.auth.Auth.generate_application_jwt', vonage_jwt_mock):
with patch('vonage_http_client.auth.Auth.generate_application_jwt', vonage_jwt_mock):
jwt = auth.generate_application_jwt()
assert jwt == test_jwt


def test_create_jwt_auth_string():
auth = Auth(application_id=application_id, private_key=private_key)
with patch('http_client.auth.Auth.generate_application_jwt', vonage_jwt_mock):
with patch('vonage_http_client.auth.Auth.generate_application_jwt', vonage_jwt_mock):
header_auth_string = auth.create_jwt_auth_string()
assert header_auth_string == b'Bearer ' + test_jwt

Expand Down
12 changes: 6 additions & 6 deletions http_client/tests/test_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
from os.path import abspath, dirname, join

import responses
from http_client.auth import Auth
from http_client.errors import (
from pytest import raises
from requests import Response
from vonage_http_client.auth import Auth
from vonage_http_client.errors import (
AuthenticationError,
HttpRequestError,
InvalidHttpClientOptionsError,
RateLimitedError,
ServerError,
)
from pytest import raises
from requests import Response
from vonage_http_client.http_client import HttpClient

from http_client.http_client import HttpClient
from testing_utils import build_response
from testutils import build_response

path = abspath(__file__)

Expand Down
7 changes: 4 additions & 3 deletions number_insight_v2/BUILD
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
resource(name='pyproject', source='pyproject.toml')
file(name='readme', source='README.md')

files(sources=['tests/data/*'])

python_distribution(
name='vonage-number-insight-v2',
dependencies=[
':pyproject',
'number_insight_v2/src/number_insight_v2',
'utils/src/utils',
':readme',
'number_insight_v2/src/vonage_number_insight_v2',
],
provides=python_artifact(),
generate_setup=False,
repositories=['https://test.pypi.org/legacy/'],
repositories=['@pypi'],
)
22 changes: 21 additions & 1 deletion number_insight_v2/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# Vonage Number Insight Python SDK package

This package contains the code to use Vonage's Number Insight API in Python.
This package contains the code to use v2 of Vonage's Number Insight API (currently in beta) in Python.

It includes classes for making fraud check requests and handling the responses.

## Usage
First, import the necessary classes and create an instance of the `NumberInsightV2` class:

```python
from vonage_http_client.http_client import HttpClient
from number_insight_v2 import NumberInsightV2, FraudCheckRequest

http_client = HttpClient(api_host='your_api_host', api_key='your_api_key', api_secret='your_api_secret')
number_insight = NumberInsightV2(http_client)
```

You can then create a `FraudCheckRequest` object and use the `fraud_check` method to initiate a fraud check request:

```python
request = FraudCheckRequest(phone='1234567890')
response = number_insight.fraud_check(request)
```
4 changes: 2 additions & 2 deletions number_insight_v2/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ readme = "README.md"
authors = [{ name = "Vonage", email = "devrel@vonage.com" }]
requires-python = ">=3.8"
dependencies = [
"vonage-http-client>=0.1.0",
"vonage-utils>=0.1.0",
"vonage-http-client>=1.0.0",
"vonage-utils>=1.0.0",
"pydantic>=2.6.1",
]
classifiers = [
Expand Down
Empty file.
7 changes: 7 additions & 0 deletions number_insight_v2/src/vonage_number_insight_v2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .number_insight_v2 import FraudCheckRequest, FraudCheckResponse, NumberInsightV2

__all__ = [
'NumberInsightV2',
'FraudCheckRequest',
'FraudCheckResponse',
]
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from utils.errors import VonageError
from vonage_utils.errors import VonageError


class NumberInsightV2Error(VonageError):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
from dataclasses import dataclass
from typing import List, Literal, Optional, Union

from pydantic import BaseModel
from pydantic import BaseModel, field_validator, validate_call
from vonage_http_client.http_client import HttpClient

from http_client.http_client import HttpClient
from vonage_utils import format_phone_number


class FraudCheckRequest(BaseModel):
Expand All @@ -14,6 +15,11 @@ class FraudCheckRequest(BaseModel):
] = ['fraud_score', 'sim_swap']
type: Literal['phone'] = 'phone'

@field_validator('phone')
@classmethod
def format_phone_number(cls, value):
return format_phone_number(value)


@dataclass
class Phone:
Expand All @@ -30,14 +36,14 @@ class FraudScore:
status: str


@dataclass()
@dataclass
class SimSwap:
status: str
swapped: Optional[bool] = None
reason: Optional[str] = None


@dataclass()
@dataclass
class FraudCheckResponse:
request_id: str
type: str
Expand All @@ -53,6 +59,7 @@ def __init__(self, http_client: HttpClient) -> None:
self._http_client = deepcopy(http_client)
self._auth_type = 'basic'

@validate_call
def fraud_check(self, request: FraudCheckRequest) -> FraudCheckResponse:
"""Initiate a fraud check request."""
response = self._http_client.post(
Expand Down
2 changes: 1 addition & 1 deletion number_insight_v2/tests/BUILD
Original file line number Diff line number Diff line change
@@ -1 +1 @@
python_tests(dependencies=['number_insight_v2'])
python_tests(dependencies=['number_insight_v2', 'testutils'])
22 changes: 16 additions & 6 deletions number_insight_v2/tests/test_number_insight_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
from os.path import abspath

import responses
from http_client.auth import Auth
from pydantic import ValidationError
from pytest import raises

from http_client.http_client import HttpClient
from number_insight_v2.number_insight_v2 import (
from vonage_http_client.auth import Auth
from vonage_http_client.http_client import HttpClient
from vonage_number_insight_v2.number_insight_v2 import (
FraudCheckRequest,
FraudCheckResponse,
NumberInsightV2,
)
from testing_utils import build_response
from utils.utils import remove_none_values
from vonage_utils.errors import InvalidPhoneNumberError
from vonage_utils.utils import remove_none_values

from testutils import build_response

path = abspath(__file__)

Expand All @@ -34,6 +35,15 @@ def test_fraud_check_request_custom_insights():
assert request.insights == ['fraud_score']


def test_fraud_check_request_invalid_phone():
with raises(InvalidPhoneNumberError):
FraudCheckRequest(phone='invalid_phone')
with raises(InvalidPhoneNumberError):
FraudCheckRequest(phone='123')
with raises(InvalidPhoneNumberError):
FraudCheckRequest(phone='12345678901234567890')


def test_fraud_check_request_invalid_insights():
with raises(ValidationError):
FraudCheckRequest(phone='1234567890', insights=['invalid_insight'])
Expand Down
4 changes: 2 additions & 2 deletions pants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ backend_packages = [
enabled = false

[source]
root_patterns = ['/', 'src/', 'tests/', 'testing_utils/']
root_patterns = ['/', 'src/', 'tests/']

[python]
interpreter_constraints = ['==3.11.*']
Expand All @@ -32,7 +32,7 @@ filter = [
'http_client/src',
'number_insight_v2/src',
'utils/src',
'testing_utils',
'testutils',
]

[black]
Expand Down
1 change: 0 additions & 1 deletion testing_utils/BUILD

This file was deleted.

Empty file removed testing_utils/__init__.py
Empty file.
1 change: 1 addition & 0 deletions testutils/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python_sources()
Loading

0 comments on commit 715e9f6

Please sign in to comment.