diff --git a/application/README.md b/application/README.md index aeb4d58..a00a796 100644 --- a/application/README.md +++ b/application/README.md @@ -8,40 +8,24 @@ It includes methods for managing applications. 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`. --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- - -### List Users - -With no custom options specified, this method will get the last 100 users. It returns a tuple consisting of a list of `UserSummary` objects and a string describing the cursor to the next page of results. +### List Applications + +With no custom options specified, this method will get the first 100 applications. It returns a tuple consisting of a list of `ApplicationData` objects and an int showing the page number of the next page of results. ```python -from vonage_users import ListUsersRequest +from vonage_application import ListApplicationsFilter, ApplicationData -users, _ = vonage_client.users.list_users() +applications, next_page = vonage_client.application.list_applications() # With options -params = ListUsersRequest( - page_size=10, - cursor=my_cursor, - order='desc', -) -users, next_cursor = vonage_client.users.list_users(params) +options = ListApplicationsFilter(page_size=3, page=2) +applications, next_page = vonage_client.applications.list_applications(options) ``` + +-------- + + ### Create a New User ```python diff --git a/application/src/vonage_application/__init__.py b/application/src/vonage_application/__init__.py index 3aa3a60..f4c43be 100644 --- a/application/src/vonage_application/__init__.py +++ b/application/src/vonage_application/__init__.py @@ -1,5 +1,45 @@ +from . import errors from .application import Application +from .common import ( + ApplicationUrl, + Capabilities, + Keys, + Messages, + MessagesWebhooks, + Privacy, + Rtc, + RtcWebhooks, + Vbc, + Verify, + VerifyWebhooks, + Voice, + VoiceUrl, + VoiceWebhooks, +) +from .enums import Region +from .requests import ApplicationConfig, ListApplicationsFilter +from .responses import ApplicationData, ListApplicationsResponse __all__ = [ 'Application', + 'ApplicationConfig', + 'ApplicationData', + 'ApplicationUrl', + 'Capabilities', + 'Keys', + 'ListApplicationsFilter', + 'ListApplicationsResponse', + 'Messages', + 'MessagesWebhooks', + 'Privacy', + 'Region', + 'Rtc', + 'RtcWebhooks', + 'Vbc', + 'Verify', + 'VerifyWebhooks', + 'Voice', + 'VoiceUrl', + 'VoiceWebhooks', + 'errors', ] diff --git a/application/src/vonage_application/application.py b/application/src/vonage_application/application.py index 51ff982..6e022f4 100644 --- a/application/src/vonage_application/application.py +++ b/application/src/vonage_application/application.py @@ -3,7 +3,7 @@ from pydantic import validate_call from vonage_http_client.http_client import HttpClient -from .requests import ApplicationOptions, ListApplicationsFilter +from .requests import ApplicationConfig, ListApplicationsFilter from .responses import ApplicationData, ListApplicationsResponse @@ -56,12 +56,13 @@ def list_applications( @validate_call def create_application( - self, params: Optional[ApplicationOptions] = None + self, params: Optional[ApplicationConfig] = None ) -> ApplicationData: """Create a new application. Args: - params (Optional[ApplicationOptions]): The application options. + params (Optional[ApplicationConfig]): Parameters describing the + application options to set. Returns: ApplicationData: The created application object. @@ -85,39 +86,40 @@ def get_application(self, id: str) -> ApplicationData: ApplicationData: The created application object. """ response = self._http_client.get( - self._http_client.api_host, f'/v1/users/{id}', None, self._auth_type + self._http_client.api_host, f'/v2/applications/{id}', None, self._auth_type ) return ApplicationData(**response) - # @validate_call - # def update_application(self, id: str, params: User) -> User: - # """Update a user. - - # Args: - # id (str): The ID of the user to update. - # params (User): The updated user object. - - # Returns: - # User: The updated user object. - # """ - # response = self._http_client.patch( - # self._http_client.api_host, - # f'/v1/users/{id}', - # params.model_dump(exclude_none=True), - # self._auth_type, - # ) - # return User(**response) - - # @validate_call - # def delete_application(self, id: str) -> None: - # """Delete an application. - - # Args: - # id (str): The ID of the application to delete. - - # Returns: - # None - # """ - # self._http_client.delete( - # self._http_client.api_host, f'/v2/applications/{id}', None, self._auth_type - # ) + @validate_call + def update_application(self, id: str, params: ApplicationConfig) -> ApplicationData: + """Update an application. + + Args: + id (str): The ID of the application to update. + params (ApplicationConfig): Parameters describing the + application options to update. + + Returns: + ApplicationData: The updated application object. + """ + response = self._http_client.put( + self._http_client.api_host, + f'/v2/applications/{id}', + params.model_dump(exclude_none=True), + self._auth_type, + ) + return ApplicationData(**response) + + @validate_call + def delete_application(self, id: str) -> None: + """Delete an application. + + Args: + id (str): The ID of the application to delete. + + Returns: + None + """ + self._http_client.delete( + self._http_client.api_host, f'/v2/applications/{id}', None, self._auth_type + ) diff --git a/application/src/vonage_application/common.py b/application/src/vonage_application/common.py index 25df53e..79cc9be 100644 --- a/application/src/vonage_application/common.py +++ b/application/src/vonage_application/common.py @@ -1,6 +1,6 @@ from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator from .enums import Region from .errors import ApplicationError @@ -103,6 +103,12 @@ class Capabilities(BaseModel): verify: Optional[Verify] = None +class Keys(BaseModel): + model_config = ConfigDict(extra='allow') + + public_key: Optional[str] = None + + class ApplicationBase(BaseModel): """Base application object used in requests and responses when communicating with the Vonage Application API.""" @@ -110,3 +116,4 @@ class ApplicationBase(BaseModel): name: str capabilities: Optional[Capabilities] = None privacy: Optional[Privacy] = None + keys: Optional[Keys] = None diff --git a/application/src/vonage_application/requests.py b/application/src/vonage_application/requests.py index 17817cf..c297a26 100644 --- a/application/src/vonage_application/requests.py +++ b/application/src/vonage_application/requests.py @@ -12,9 +12,5 @@ class ListApplicationsFilter(BaseModel): page: int = None -class RequestKeys(BaseModel): - public_key: str - - -class ApplicationOptions(ApplicationBase): - keys: Optional[RequestKeys] = None +class ApplicationConfig(ApplicationBase): + pass diff --git a/application/src/vonage_application/responses.py b/application/src/vonage_application/responses.py index 7574f4e..de7634b 100644 --- a/application/src/vonage_application/responses.py +++ b/application/src/vonage_application/responses.py @@ -3,17 +3,12 @@ from pydantic import BaseModel, Field, model_validator from vonage_utils.models import HalLinks, ResourceLink -from .common import ApplicationBase - - -class ResponseKeys(BaseModel): - public_key: Optional[str] = None - private_key: Optional[str] = None +from .common import ApplicationBase, Keys class ApplicationData(ApplicationBase): id: str - keys: Optional[ResponseKeys] = None + keys: Optional[Keys] = None links: Optional[ResourceLink] = Field(None, validation_alias='_links', exclude=True) link: Optional[str] = None diff --git a/application/tests/data/get_application.json b/application/tests/data/get_application.json new file mode 100644 index 0000000..d3239e3 --- /dev/null +++ b/application/tests/data/get_application.json @@ -0,0 +1,36 @@ +{ + "id": "1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b", + "name": "My Server Demo", + "keys": { + "public_key": "-----BEGIN PUBLIC KEY-----\npublic_key_info_goes_here\n-----END PUBLIC KEY-----\n" + }, + "privacy": { + "improve_ai": false + }, + "capabilities": { + "voice": { + "webhooks": { + "event_url": { + "address": "http://example.ngrok.app/webhooks/events", + "http_method": "POST", + "socket_timeout": 10000, + "connect_timeout": 1000 + }, + "answer_url": { + "address": "http://example.ngrok.app/webhooks/answer", + "http_method": "GET", + "socket_timeout": 5000, + "connect_timeout": 1000 + } + }, + "signed_callbacks": true, + "conversations_ttl": 48, + "leg_persistence_time": 7 + } + }, + "_links": { + "self": { + "href": "/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b" + } + } +} \ No newline at end of file diff --git a/application/tests/data/update_application.json b/application/tests/data/update_application.json new file mode 100644 index 0000000..335ea70 --- /dev/null +++ b/application/tests/data/update_application.json @@ -0,0 +1,13 @@ +{ + "id": "1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b", + "name": "My Updated Application", + "keys": { + "public_key": "-----BEGIN PUBLIC KEY-----\nupdated_public_key_info\n-----END PUBLIC KEY-----\n" + }, + "capabilities": {}, + "_links": { + "self": { + "href": "/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b" + } + } +} \ No newline at end of file diff --git a/application/tests/test_application.py b/application/tests/test_application.py index 4d9cbf0..6c14a4c 100644 --- a/application/tests/test_application.py +++ b/application/tests/test_application.py @@ -6,6 +6,7 @@ from vonage_application.common import ( ApplicationUrl, Capabilities, + Keys, Messages, MessagesWebhooks, Privacy, @@ -20,11 +21,7 @@ ) from vonage_application.enums import Region from vonage_application.errors import ApplicationError -from vonage_application.requests import ( - ApplicationOptions, - ListApplicationsFilter, - RequestKeys, -) +from vonage_application.requests import ApplicationConfig, ListApplicationsFilter from vonage_http_client.http_client import HttpClient from testutils import build_response, get_mock_api_key_auth @@ -47,7 +44,7 @@ def test_create_application_basic(): 'https://api.nexmo.com/v2/applications', 'create_application_basic.json', ) - app = application.create_application(ApplicationOptions(name='My Application')) + app = application.create_application(ApplicationConfig(name='My Application')) assert app.id == 'ba1a6aa3-8ac6-487d-ac5c-be469e77ddb7' assert app.name == 'My Application' @@ -132,7 +129,7 @@ def test_create_application_options_model_from_dict(): 'keys': keys, } application_options_dict = params - application_options_model = ApplicationOptions(**application_options_dict) + application_options_model = ApplicationConfig(**application_options_dict) assert ( application_options_model.model_dump(exclude_unset=True) == application_options_dict @@ -212,9 +209,9 @@ def test_create_application_options_with_models(): privacy = Privacy(improve_ai=False) public_key = '-----BEGIN PUBLIC KEY-----\npublic_key_info_goes_here\n-----END PUBLIC KEY-----\n' - keys = RequestKeys(public_key=public_key) + keys = Keys(public_key=public_key) - params = ApplicationOptions( + params = ApplicationConfig( name='My Customised Application', capabilities=capabilities, privacy=privacy, @@ -307,3 +304,54 @@ def test_list_applications_multiple_pages(): ) assert applications[2].id == '3b3b3b3b-3b3b-3b3b-3b3b-3b3b3b3b3b3b' assert next_page == 2 + + +@responses.activate +def test_get_application(): + build_response( + path, + 'GET', + 'https://api.nexmo.com/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b', + 'get_application.json', + ) + app = application.get_application('1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b') + + assert app.id == '1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b' + assert app.link == '/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b' + + +@responses.activate +def test_update_application(): + build_response( + path, + 'PUT', + 'https://api.nexmo.com/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b', + 'update_application.json', + ) + + public_key = ( + '-----BEGIN PUBLIC KEY-----\nupdated_public_key_info\n-----END PUBLIC KEY-----\n' + ) + keys = Keys(public_key=public_key) + params = ApplicationConfig(name='My Updated Application', keys=keys) + application_data = application.update_application( + '1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b', params + ) + + assert application_data.name == 'My Updated Application' + assert application_data.keys.public_key == public_key + assert ( + application_data.link == '/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b' + ) + + +@responses.activate +def test_delete_application(): + responses.add( + responses.DELETE, + 'https://api.nexmo.com/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b', + status=204, + ) + + application.delete_application('1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b') + assert application.http_client.last_response.status_code == 204