Skip to content

Commit

Permalink
finish create method, add list method and tests, refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkahan committed May 2, 2024
1 parent 10113f0 commit ec0d9f2
Show file tree
Hide file tree
Showing 11 changed files with 574 additions and 139 deletions.
28 changes: 19 additions & 9 deletions application/src/vonage_application/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from vonage_http_client.http_client import HttpClient

from .requests import ApplicationOptions, ListApplicationsFilter
from .responses import ApplicationData
from .responses import ApplicationData, ListApplicationsResponse


class Application:
Expand All @@ -27,22 +27,32 @@ def http_client(self) -> HttpClient:
def list_applications(
self, filter: ListApplicationsFilter = ListApplicationsFilter()
) -> Tuple[List[ApplicationData], Optional[str]]:
""""""
"""List applications.
By default, returns the first 100 applications and the page index of
the next page of results, if there are more than 100 applications.
Args:
filter (ListApplicationsFilter): The filter object.
Returns:
Tuple[List[ApplicationData], Optional[str]]: A tuple containing a
list of applications and the next page index.
"""
response = self._http_client.get(
self._http_client.api_host,
'/v2/applications',
filter.model_dump(exclude_none=True),
self._auth_type,
)

# applications_response = ListApplicationsResponse(**response)
# if applications_response.links.next is None:
# return applications_response.embedded.users, None
applications_response = ListApplicationsResponse(**response)

if applications_response.page == applications_response.total_pages:
return applications_response.embedded.applications, None

# parsed_url = urlparse(users_response.links.next.href)
# query_params = parse_qs(parsed_url.query)
# next_cursor = query_params.get('cursor', [None])[0]
# return users_response.embedded.users, next_cursor
next_page = applications_response.page + 1
return applications_response.embedded.applications, next_page

@validate_call
def create_application(
Expand Down
65 changes: 49 additions & 16 deletions application/src/vonage_application/common.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
from typing import Literal, Optional, Union
from typing import Literal, Optional

from pydantic import BaseModel, Field, field_validator

from .enums import Region
from .errors import ApplicationError


class Url(BaseModel):
class ApplicationUrl(BaseModel):
address: str
http_method: Optional[Literal['GET', 'POST']] = None


class VoiceUrl(Url):
connection_timeout: Optional[int] = Field(None, ge=300, le=1000)
socket_timeout: Optional[int] = Field(None, ge=1000, le=5000)
class VoiceUrl(ApplicationUrl):
connect_timeout: Optional[int] = Field(None, ge=300, le=1000)
socket_timeout: Optional[int] = Field(None, ge=1000, le=10000)


class VoiceWebhooks(BaseModel):
answer_url: Optional[Url] = None
fallback_answer_url: Optional[Url] = None
event_url: Optional[Url] = None
answer_url: Optional[VoiceUrl] = None
fallback_answer_url: Optional[VoiceUrl] = None
event_url: Optional[VoiceUrl] = None


class Voice(BaseModel):
"""Voice application capabilities."""

webhooks: Optional[VoiceWebhooks] = None
signed_callbacks: Optional[bool] = None
conversations_ttl: Optional[int] = Field(None, ge=1, le=9000)
Expand All @@ -31,40 +33,60 @@ class Voice(BaseModel):


class RtcWebhooks(BaseModel):
event_url: Optional[Url] = None
event_url: Optional[ApplicationUrl] = None


class Rtc(BaseModel):
"""Real-Time Communications application capabilities."""

webhooks: Optional[RtcWebhooks] = None
signed_callbacks: Optional[bool] = None


class MessagesWebhooks(BaseModel):
inbound_url: Optional[Url] = None
status_url: Optional[Url] = None
inbound_url: Optional[ApplicationUrl] = None
status_url: Optional[ApplicationUrl] = None

@field_validator('inbound_url', 'status_url')
@classmethod
def check_http_method(cls, v: ApplicationUrl):
if v.http_method is not None and v.http_method != 'POST':
raise ApplicationError('HTTP method must be POST')
return v


class Messages(BaseModel):
version: Optional[str] = None
"""Messages application capabilities."""

webhooks: Optional[MessagesWebhooks] = None
version: Optional[str] = None
authenticate_inbound_media: Optional[bool] = None


class Vbc(BaseModel):
pass
"""VBC capabilities.
This object should be empty when creating or updating an application.
"""


class VerifyWebhooks(BaseModel):
status_url: Optional[Url] = None
status_url: Optional[ApplicationUrl] = None

@field_validator('status_url')
@classmethod
def check_http_method(cls, v: Url):
def check_http_method(cls, v: ApplicationUrl):
if v.http_method is not None and v.http_method != 'POST':
raise ApplicationError('HTTP method must be POST')
return v


class Verify(BaseModel):
"""Verify application capabilities.
Don't set the `version` field when creating or updating an application.
"""

webhooks: Optional[VerifyWebhooks] = None
version: Optional[str] = None

Expand All @@ -73,7 +95,18 @@ class Privacy(BaseModel):
improve_ai: Optional[bool] = None


class Capabilities(BaseModel):
voice: Optional[Voice] = None
rtc: Optional[Rtc] = None
messages: Optional[Messages] = None
vbc: Optional[Vbc] = None
verify: Optional[Verify] = None


class ApplicationBase(BaseModel):
"""Base application object used in requests and responses when communicating with the Vonage
Application API."""

name: str
capabilities: Optional[Union[Voice, Rtc, Messages, Vbc, Verify]] = None
capabilities: Optional[Capabilities] = None
privacy: Optional[Privacy] = None
6 changes: 3 additions & 3 deletions application/src/vonage_application/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@


class ListApplicationsFilter(BaseModel):
"""Request object for listing users."""
"""Request object for filtering applications."""

page_size: Optional[int] = 100
page: int = None


class KeysRequest(BaseModel):
class RequestKeys(BaseModel):
public_key: str


class ApplicationOptions(ApplicationBase):
keys: Optional[KeysRequest] = None
keys: Optional[RequestKeys] = None
30 changes: 17 additions & 13 deletions application/src/vonage_application/responses.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
from typing import Optional
from typing import List, Optional

from pydantic import BaseModel, Field, model_validator
from vonage_utils.models import ResourceLink
from vonage_utils.models import HalLinks, ResourceLink

from .common import ApplicationBase

# class Embedded(BaseModel):
# users: List[UserSummary] = []


# class ListApplicationsResponse(BaseModel):
# page_size: int
# embedded: Embedded = Field(..., validation_alias='_embedded')
# links: Links = Field(..., validation_alias='_links')


class KeysResponse(BaseModel):
class ResponseKeys(BaseModel):
public_key: Optional[str] = None
private_key: Optional[str] = None


class ApplicationData(ApplicationBase):
id: str
keys: Optional[KeysResponse] = None
keys: Optional[ResponseKeys] = None
links: Optional[ResourceLink] = Field(None, validation_alias='_links', exclude=True)
link: Optional[str] = None

Expand All @@ -31,3 +22,16 @@ def get_link(self):
if self.links is not None:
self.link = self.links.self.href
return self


class Embedded(BaseModel):
applications: List[ApplicationData] = []


class ListApplicationsResponse(BaseModel):
page_size: Optional[int] = None
page: int = Field(None, ge=1)
total_items: Optional[int] = None
total_pages: Optional[int] = None
embedded: Embedded = Field(..., validation_alias='_embedded')
links: HalLinks = Field(..., validation_alias='_links')
68 changes: 63 additions & 5 deletions application/tests/data/create_application_options.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,75 @@
{
"id": "ba1a6aa3-8ac6-487d-ac5c-be469e77ddb7",
"name": "My Application",
"id": "33e3329f-d1cc-48f3-9105-55e5a6e475c1",
"name": "My Customised Application",
"keys": {
"private_key": "-----BEGIN PRIVATE KEY-----\nprivate_key_info_goes_here\n-----END PRIVATE KEY-----\n",
"public_key": "-----BEGIN PUBLIC KEY-----\npublic_key_info_goes_here\n-----END PUBLIC KEY-----\n"
},
"privacy": {
"improve_ai": false
},
"capabilities": {},
"capabilities": {
"voice": {
"webhooks": {
"event_url": {
"address": "https://example.com/event",
"http_method": "POST",
"socket_timeout": 3000,
"connect_timeout": 500
},
"answer_url": {
"address": "https://example.com/answer",
"http_method": "POST",
"socket_timeout": 3000,
"connect_timeout": 500
},
"fallback_answer_url": {
"address": "https://example.com/fallback",
"http_method": "POST",
"socket_timeout": 3000,
"connect_timeout": 500
}
},
"signed_callbacks": true,
"conversations_ttl": 8000,
"leg_persistence_time": 14,
"region": "na-east"
},
"rtc": {
"webhooks": {
"event_url": {
"address": "https://example.com/event",
"http_method": "POST"
}
},
"signed_callbacks": true
},
"messages": {
"webhooks": {
"inbound_url": {
"address": "https://example.com/inbound",
"http_method": "POST"
},
"status_url": {
"address": "https://example.com/status",
"http_method": "POST"
}
},
"version": "v1",
"authenticate_inbound_media": true
},
"verify": {
"webhooks": {
"status_url": {
"address": "https://example.com/status",
"http_method": "POST"
}
}
},
"vbc": {}
},
"_links": {
"self": {
"href": "/v2/applications/ba1a6aa3-8ac6-487d-ac5c-be469e77ddb7"
"href": "/v2/applications/33e3329f-d1cc-48f3-9105-55e5a6e475c1"
}
}
}
48 changes: 48 additions & 0 deletions application/tests/data/list_applications_basic.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"page_size": 100,
"page": 1,
"total_items": 1,
"total_pages": 1,
"_embedded": {
"applications": [
{
"id": "1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b",
"name": "dev-application",
"keys": {
"public_key": "-----BEGIN PUBLIC KEY-----\npublic_key_info_goes_here\n-----END PUBLIC KEY-----\n"
},
"privacy": {
"improve_ai": true
},
"capabilities": {
"voice": {
"webhooks": {
"event_url": {
"address": "http://example.com",
"http_method": "POST"
},
"answer_url": {
"address": "http://example.com",
"http_method": "GET"
}
},
"signed_callbacks": true,
"conversations_ttl": 9000,
"leg_persistence_time": 7
}
}
}
]
},
"_links": {
"self": {
"href": "/v2/applications?page_size=100&page=1"
},
"first": {
"href": "/v2/applications?page_size=100"
},
"last": {
"href": "/v2/applications?page_size=100&page=1"
}
}
}
Loading

0 comments on commit ec0d9f2

Please sign in to comment.