Skip to content

Commit

Permalink
add subaccounts api
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkahan committed Aug 30, 2024
1 parent 4a27754 commit f5c8ca8
Show file tree
Hide file tree
Showing 17 changed files with 861 additions and 225 deletions.
89 changes: 63 additions & 26 deletions subaccounts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,102 @@

This package contains the code to use Vonage's Subaccount API in Python.

It includes methods for managing Vonage subaccounts.
It includes methods for creating and modifying Vonage subaccounts and transferring credit, balances and numbers between subaccounts.

## Usage

It is recommended to use this as part of the main `vonage` package. The examples below assume you've created an instance of the `vonage.Vonage` class called `vonage_client`.


<!-- ### Get Account Balance
### List Subaccounts

```python
balance = vonage_client.account.get_balance()
print(balance)
response = vonage_client.subaccounts.list_subaccounts()
print(response.model_dump)
```

### Top-Up Account
### Create Subaccount

```python
response = vonage_client.account.top_up(trx='1234567890')
from vonage_subaccounts import SubaccountOptions

response = vonage_client.subaccounts.create_subaccount(
SubaccountOptions(
name='test_subaccount', secret='1234asdfA', use_primary_account_balance=False
)
)
print(response)
```

### Update the Default SMS Webhook
This will return a Pydantic object (`SettingsResponse`) containing multiple settings for your account.
### Modify a Subaccount

```python
settings: SettingsResponse = vonage_client.account.update_default_sms_webhook(
mo_callback_url='https://example.com/inbound_sms_webhook',
dr_callback_url='https://example.com/delivery_receipt_webhook',
from vonage_subaccounts import ModifySubaccountOptions

response = vonage_client.subaccounts.modify_subaccount(
'test_subaccount',
ModifySubaccountOptions(
suspended=True,
name='modified_test_subaccount',
),
)
print(response)
```

print(settings)
### List Balance Transfers

```python
from vonage_subaccounts import ListTransfersFilter

filter = {'start_date': '2023-08-07T10:50:44Z'}
response = vonage_client.subaccounts.list_balance_transfers(ListTransfersFilter(**filter))
for item in response:
print(item.model_dump())
```

### List Secrets Associated with the Account
### Transfer Balance Between Subaccounts

```python
response = vonage_client.account.list_secrets()
from vonage_subaccounts import TransferRequest

request = TransferRequest(
from_='test_api_key', to='test_subaccount', amount=0.02, reference='A reference'
)
response = vonage_client.subaccounts.transfer_balance(request)
print(response)
```

### Create a New Account Secret
### List Credit Transfers

```python
secret = vonage_client.account.create_secret('Mytestsecret12345')
print(secret)
from vonage_subaccounts import ListTransfersFilter

filter = {'start_date': '2023-08-07T10:50:44Z'}
response = vonage_client.subaccounts.list_credit_transfers(ListTransfersFilter(**filter))
for item in response:
print(item.model_dump())
```

### Get Information About One Secret
### Transfer Credit Between Subaccounts

```python
secret = vonage_client.account.get_secret(MY_SECRET_ID)
print(secret)
```
from vonage_subaccounts import TransferRequest

### Revoke a Secret
request = TransferRequest(
from_='test_api_key', to='test_subaccount', amount=0.02, reference='A reference'
)
response = vonage_client.subaccounts.transfer_balance(request)
print(response)
```

Note: it isn't possible to revoke all account secrets, there must always be one valid secret. Attempting to do so will give a 403 error.
### Transfer a Phone Number Between Subaccounts

```python
client.account.revoke_secret(MY_SECRET_ID)
``` -->
from vonage_subaccounts import TransferNumberRequest

request = TransferNumberRequest(
from_='test_api_key', to='test_subaccount', number='447700900000', country='GB'
)
response = vonage_client.subaccounts.transfer_number(request)
print(response)
```
30 changes: 30 additions & 0 deletions subaccounts/src/vonage_subaccounts/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
from .errors import InvalidSecretError
from .requests import (
ListTransfersFilter,
ModifySubaccountOptions,
SubaccountOptions,
TransferNumberRequest,
TransferRequest,
)
from .responses import (
ListSubaccountsResponse,
NewSubaccount,
PrimaryAccount,
Subaccount,
Transfer,
TransferNumberResponse,
VonageAccount,
)
from .subaccounts import Subaccounts

__all__ = [
'Subaccounts',
'InvalidSecretError',
'ListTransfersFilter',
'SubaccountOptions',
'ModifySubaccountOptions',
'TransferNumberRequest',
'TransferRequest',
'VonageAccount',
'PrimaryAccount',
'Subaccount',
'ListSubaccountsResponse',
'NewSubaccount',
'Transfer',
'TransferNumberResponse',
]
4 changes: 2 additions & 2 deletions subaccounts/src/vonage_subaccounts/errors.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from vonage_utils.errors import VonageError


class SubaccountsError(VonageError):
"""Indicates an error with the Subaccounts API package."""
class InvalidSecretError(VonageError):
"""Indicates that the secret provided was invalid."""
59 changes: 59 additions & 0 deletions subaccounts/src/vonage_subaccounts/requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import re
from typing import Optional

from pydantic import BaseModel, Field, field_validator
from vonage_subaccounts.errors import InvalidSecretError


class SubaccountOptions(BaseModel):
name: str = Field(..., min_length=1, max_length=80)
secret: Optional[str] = None
use_primary_account_balance: Optional[bool] = None

@field_validator('secret')
@classmethod
def check_valid_secret(cls, v):
if not _is_valid_secret(v):
raise InvalidSecretError(
'Secret must be 8-25 characters long and contain at least one uppercase '
'letter, one lowercase letter, and one digit.'
)
return v


def _is_valid_secret(secret: str) -> bool:
if len(secret) < 8 or len(secret) > 25:
return False
if not re.search(r'[a-z]', secret):
return False
if not re.search(r'[A-Z]', secret):
return False
if not re.search(r'\d', secret):
return False
return True


class ModifySubaccountOptions(BaseModel):
suspended: Optional[bool] = None
use_primary_account_balance: Optional[bool] = None
name: Optional[str] = None


class ListTransfersFilter(BaseModel):
start_date: str
end_date: Optional[str] = None
subaccount: Optional[str] = None


class TransferRequest(BaseModel):
from_: str = Field(..., serialization_alias='from')
to: str
amount: float
reference: Optional[str] = None


class TransferNumberRequest(BaseModel):
from_: str = Field(..., serialization_alias='from')
to: str
number: str
country: Optional[str] = None
43 changes: 35 additions & 8 deletions subaccounts/src/vonage_subaccounts/responses.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,48 @@
from datetime import datetime
from typing import Optional
from typing import Optional, Union

from pydantic import BaseModel, Field


class VonageAccount(BaseModel):
api_key: str
name: str
primary_account_api_key: str
use_primary_account_balance: bool
created_at: datetime
created_at: str
suspended: bool
balance: Optional[float]
credit_limit: Optional[float]
credit_limit: Optional[Union[int, float]]


class PrimaryAccount(VonageAccount):
...


class Subaccount(VonageAccount):
primary_account_api_key: str
use_primary_account_balance: bool


class ListSubaccountsResponse(BaseModel):
primary_account: PrimaryAccount
subaccounts: list[Subaccount]
total_balance: float
total_credit_limit: Union[int, float]


class NewSubaccount(Subaccount):
secret: str


class PrimaryAccount(VonageAccount): ...
class Transfer(BaseModel):
id: str
amount: float
from_: str = Field(..., validation_alias='from')
to: str
created_at: str
reference: Optional[str] = None


class Subaccount(VonageAccount): ...
class TransferNumberResponse(BaseModel):
number: str
country: str
from_: str = Field(..., validation_alias='from')
to: str
Loading

0 comments on commit f5c8ca8

Please sign in to comment.