From 03b2741cff4c68a3ca288d3f2b8df1768ae145b1 Mon Sep 17 00:00:00 2001 From: maxkahan Date: Mon, 14 Aug 2023 15:57:10 +0100 Subject: [PATCH] Add Users (#269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add users api and tests * updated README * updated changelog * Bump version: 3.7.1 → 3.8.0 --- .bumpversion.cfg | 2 +- CHANGES.md | 3 + README.md | 37 +++ setup.py | 2 +- src/vonage/__init__.py | 2 +- src/vonage/client.py | 2 + src/vonage/errors.py | 4 + src/vonage/users.py | 72 ++++ tests/data/users/invalid_content_type.json | 13 + tests/data/users/list_users_400.json | 13 + tests/data/users/list_users_404.json | 7 + tests/data/users/list_users_500.json | 7 + tests/data/users/list_users_basic.json | 43 +++ tests/data/users/list_users_options.json | 37 +++ tests/data/users/rate_limit.json | 7 + tests/data/users/user_400.json | 13 + tests/data/users/user_404.json | 7 + tests/data/users/user_basic.json | 13 + tests/data/users/user_options.json | 69 ++++ tests/data/users/user_updated.json | 19 ++ tests/test_users.py | 369 +++++++++++++++++++++ 21 files changed, 738 insertions(+), 3 deletions(-) create mode 100644 src/vonage/users.py create mode 100644 tests/data/users/invalid_content_type.json create mode 100644 tests/data/users/list_users_400.json create mode 100644 tests/data/users/list_users_404.json create mode 100644 tests/data/users/list_users_500.json create mode 100644 tests/data/users/list_users_basic.json create mode 100644 tests/data/users/list_users_options.json create mode 100644 tests/data/users/rate_limit.json create mode 100644 tests/data/users/user_400.json create mode 100644 tests/data/users/user_404.json create mode 100644 tests/data/users/user_basic.json create mode 100644 tests/data/users/user_options.json create mode 100644 tests/data/users/user_updated.json create mode 100644 tests/test_users.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6a9174da..cbe68d89 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.7.1 +current_version = 3.8.0 commit = True tag = False diff --git a/CHANGES.md b/CHANGES.md index 033c59be..37cd65cb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# 3.8.0 +- Adding support for the [Users component of the Vonage Application API](https://developer.vonage.com/en/api/application.v2#User) + # 3.7.1 - Fixing dependency version to a specific major version diff --git a/README.md b/README.md index 38586894..298e41fd 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ need a Vonage account. Sign up [for free at vonage.com][signup]. - [Pricing API](#pricing-api) - [Managing Secrets](#managing-secrets) - [Application API](#application-api) +- [Users API](#users-api) - [Validating Webhook Signatures](#validate-webhook-signatures) - [JWT Parameters](#jwt-parameters) - [Overriding API Attributes](#overriding-api-attributes) @@ -1082,6 +1083,42 @@ response = client.application.delete_application(uuid) Docs: [https://developer.nexmo.com/api/application.v2#deleteApplication](https://developer.nexmo.com/api/application.v2#deleteApplication?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#destroy-an-application) + +## Users API + +These API methods are part of the [Application (v2) API](https://developer.vonage.com/en/application/overview) but are a in separate module in the SDK. [See the API reference for more details](https://developer.vonage.com/en/api/application.v2#User). + +### List all Users + +```python +client.users.list_users() +``` + +### Create a new user + +```python +client.users.create_user() # Default values generated +client.users.create_user(params={...}) # Specify custom values +``` + +### Get detailed information about a user + +```python +client.users.get_user('USER_ID') +``` + +### Update user details + +```python +client.users.update_user('USER_ID', params={...}) +``` + +### Delete a user + +```python +client.users.delete_user('USER_ID') +``` + ## Validate webhook signatures ```python diff --git a/setup.py b/setup.py index 68f71406..6a6d5456 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="vonage", - version="3.7.1", + version="3.8.0", description="Vonage Server SDK for Python", long_description=long_description, long_description_content_type="text/markdown", diff --git a/src/vonage/__init__.py b/src/vonage/__init__.py index 6b725ab8..144013f4 100644 --- a/src/vonage/__init__.py +++ b/src/vonage/__init__.py @@ -1,4 +1,4 @@ from .client import * from .ncco_builder.ncco import * -__version__ = "3.7.1" +__version__ = "3.8.0" diff --git a/src/vonage/client.py b/src/vonage/client.py index 72f0d4aa..6aa43d62 100644 --- a/src/vonage/client.py +++ b/src/vonage/client.py @@ -13,6 +13,7 @@ from .short_codes import ShortCodes from .sms import Sms from .subaccounts import Subaccounts +from .users import Users from .ussd import Ussd from .voice import Voice from .verify import Verify @@ -122,6 +123,7 @@ def __init__( self.short_codes = ShortCodes(self) self.sms = Sms(self) self.subaccounts = Subaccounts(self) + self.users = Users(self) self.ussd = Ussd(self) self.verify = Verify(self) self.verify2 = Verify2(self) diff --git a/src/vonage/errors.py b/src/vonage/errors.py index f3f0f8da..677aa366 100644 --- a/src/vonage/errors.py +++ b/src/vonage/errors.py @@ -50,3 +50,7 @@ class SubaccountsError(ClientError): class ProactiveConnectError(ClientError): """An error relating to the Proactive Connect API.""" + + +class UsersError(ClientError): + """An error relating to the Users API.""" diff --git a/src/vonage/users.py b/src/vonage/users.py new file mode 100644 index 00000000..444e03d5 --- /dev/null +++ b/src/vonage/users.py @@ -0,0 +1,72 @@ +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from vonage import Client + +from .errors import UsersError +from ._internal import set_auth_type + + +class Users: + """Class containing methods for user management as part of the Application API.""" + + def __init__(self, client: Client): + self._client = client + self._auth_type = set_auth_type(self._client) + + def list_users( + self, + page_size: int = None, + order: str = 'asc', + cursor: str = None, + name: str = None, + ): + """ + Lists the name and user id of all users associated with the account. + For complete information on a user, call Users.get_user, passing in the user id. + """ + + if order.lower() not in ('asc', 'desc'): + raise UsersError( + 'Invalid order parameter. Must be one of: "asc", "desc", "ASC", "DESC".' + ) + + params = {'page_size': page_size, 'order': order.lower(), 'cursor': cursor, 'name': name} + return self._client.get( + self._client.api_host(), + '/v1/users', + params, + auth_type=self._auth_type, + ) + + def create_user(self, params: dict = None): + self._client.headers['Content-Type'] = 'application/json' + return self._client.post( + self._client.api_host(), + '/v1/users', + params, + auth_type=self._auth_type, + ) + + def get_user(self, user_id: str): + return self._client.get( + self._client.api_host(), + f'/v1/users/{user_id}', + auth_type=self._auth_type, + ) + + def update_user(self, user_id: str, params: dict): + return self._client.patch( + self._client.api_host(), + f'/v1/users/{user_id}', + params, + auth_type=self._auth_type, + ) + + def delete_user(self, user_id: str): + return self._client.delete( + self._client.api_host(), + f'/v1/users/{user_id}', + auth_type=self._auth_type, + ) diff --git a/tests/data/users/invalid_content_type.json b/tests/data/users/invalid_content_type.json new file mode 100644 index 00000000..d818e1a2 --- /dev/null +++ b/tests/data/users/invalid_content_type.json @@ -0,0 +1,13 @@ +{ + "title": "Bad request.", + "type": "https://developer.nexmo.com/api/conversation#http:error:validation-fail", + "code": "http:error:validation-fail", + "detail": "Invalid Content-Type.", + "instance": "9d0e245d-fac0-450e-811f-52343041df61", + "invalid_parameters": [ + { + "name": "content-type", + "reason": "content-type \"application/octet-stream\" is not supported. Supported versions are [application/json]" + } + ] +} \ No newline at end of file diff --git a/tests/data/users/list_users_400.json b/tests/data/users/list_users_400.json new file mode 100644 index 00000000..10b68249 --- /dev/null +++ b/tests/data/users/list_users_400.json @@ -0,0 +1,13 @@ +{ + "title": "Bad request.", + "type": "https://developer.nexmo.com/api/conversation#http:error:validation-fail", + "code": "http:error:validation-fail", + "detail": "Input validation failure.", + "instance": "04ee4d32-78c9-4acf-bdc1-b7d1fa860c92", + "invalid_parameters": [ + { + "name": "page_size", + "reason": "\"page_size\" must be a number" + } + ] +} \ No newline at end of file diff --git a/tests/data/users/list_users_404.json b/tests/data/users/list_users_404.json new file mode 100644 index 00000000..7e985e23 --- /dev/null +++ b/tests/data/users/list_users_404.json @@ -0,0 +1,7 @@ +{ + "title": "Not found.", + "type": "https://developer.nexmo.com/api/conversation#user:error:not-found", + "code": "user:error:not-found", + "detail": "User does not exist, or you do not have access.", + "instance": "29c78817-eeb9-4de0-b2f9-a5ca816bc907" +} \ No newline at end of file diff --git a/tests/data/users/list_users_500.json b/tests/data/users/list_users_500.json new file mode 100644 index 00000000..25aa46c5 --- /dev/null +++ b/tests/data/users/list_users_500.json @@ -0,0 +1,7 @@ +{ + "title": "Internal Error.", + "type": "https://developer.nexmo.com/api/conversation#system:error:internal-error", + "code": "system:error:internal-error", + "detail": "Something went wrong.", + "instance": "00a5916655d650e920ccf0daf40ef4ee" +} \ No newline at end of file diff --git a/tests/data/users/list_users_basic.json b/tests/data/users/list_users_basic.json new file mode 100644 index 00000000..ec7bf4ad --- /dev/null +++ b/tests/data/users/list_users_basic.json @@ -0,0 +1,43 @@ +{ + "page_size": 10, + "_embedded": { + "users": [ + { + "id": "USR-2af4d3c5-ec49-4c4a-b74c-ec13ab560af8", + "name": "NAM-6dd4ea1f-3841-47cb-a3d3-e271f5c1e33c", + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-2af4d3c5-ec49-4c4a-b74c-ec13ab560af8" + } + } + }, + { + "id": "USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5", + "name": "NAM-ecb938f2-13e0-40c1-9d3b-b16ebb4ef3d1", + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5" + } + } + }, + { + "id": "USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422", + "name": "my_user_name", + "display_name": "My User Name", + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422" + } + } + } + ] + }, + "_links": { + "first": { + "href": "https://api-us-3.vonage.com/v1/users?order=asc&page_size=10" + }, + "self": { + "href": "https://api-us-3.vonage.com/v1/users?order=asc&page_size=10&cursor=QAuYbTXFALruTxAIRAKiHvdCAqJQjTuYkDNhN9PYWcDajgUTgd9lQPo%3D" + } + } +} \ No newline at end of file diff --git a/tests/data/users/list_users_options.json b/tests/data/users/list_users_options.json new file mode 100644 index 00000000..3c2e74d8 --- /dev/null +++ b/tests/data/users/list_users_options.json @@ -0,0 +1,37 @@ +{ + "page_size": 2, + "_embedded": { + "users": [ + { + "id": "USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422", + "name": "my_user_name", + "display_name": "My User Name", + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422" + } + } + }, + { + "id": "USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5", + "name": "NAM-ecb938f2-13e0-40c1-9d3b-b16ebb4ef3d1", + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5" + } + } + } + ] + }, + "_links": { + "first": { + "href": "https://api-us-3.vonage.com/v1/users?order=desc&page_size=2" + }, + "self": { + "href": "https://api-us-3.vonage.com/v1/users?order=desc&page_size=2&cursor=Tw2iIH8ISR4SuJRJUrK9xC78rhfI10HHRKOZ20zBN9A8SDiczcOqBj8%3D" + }, + "next": { + "href": "https://api-us-3.vonage.com/v1/users?order=desc&page_size=2&cursor=FBWj1Oxid%2FVkxP6BT%2FCwMZZ2C0uOby0QXCrebkNoNo4A3PU%2FQTOOoD%2BWHib6ewsVLygsQBJy7di8HI9m30A3ujVuv1578w4Lqitgbv6CAnxdzPMeLCcAxNYWxl8%3D" + } + } +} \ No newline at end of file diff --git a/tests/data/users/rate_limit.json b/tests/data/users/rate_limit.json new file mode 100644 index 00000000..679dc079 --- /dev/null +++ b/tests/data/users/rate_limit.json @@ -0,0 +1,7 @@ +{ + "title": "Too Many Requests.", + "type": "https://developer.nexmo.com/api/conversation#http:error:too-many-request", + "code": "http:error:too-many-request", + "detail": "You have exceeded your request limit. You can try again shortly.", + "instance": "00a5916655d650e920ccf0daf40ef4ee" +} \ No newline at end of file diff --git a/tests/data/users/user_400.json b/tests/data/users/user_400.json new file mode 100644 index 00000000..6269d4f7 --- /dev/null +++ b/tests/data/users/user_400.json @@ -0,0 +1,13 @@ +{ + "title": "Bad request.", + "type": "https://developer.nexmo.com/api/conversation#http:error:validation-fail", + "code": "http:error:validation-fail", + "detail": "Input validation failure.", + "instance": "00a5916655d650e920ccf0daf40ef4ee", + "invalid_parameters": [ + { + "name": "name", + "reason": "\"name\" must be a string" + } + ] +} \ No newline at end of file diff --git a/tests/data/users/user_404.json b/tests/data/users/user_404.json new file mode 100644 index 00000000..cde74ea9 --- /dev/null +++ b/tests/data/users/user_404.json @@ -0,0 +1,7 @@ +{ + "title": "Not found.", + "type": "https://developer.nexmo.com/api/conversation#user:error:not-found", + "code": "user:error:not-found", + "detail": "User does not exist, or you do not have access.", + "instance": "9b3b0ea8-987a-4117-b75a-8425e04910c4" +} \ No newline at end of file diff --git a/tests/data/users/user_basic.json b/tests/data/users/user_basic.json new file mode 100644 index 00000000..794c3102 --- /dev/null +++ b/tests/data/users/user_basic.json @@ -0,0 +1,13 @@ +{ + "id": "USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5", + "name": "NAM-ecb938f2-13e0-40c1-9d3b-b16ebb4ef3d1", + "properties": { + "custom_data": {} + }, + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5" + } + }, + "channels": {} +} \ No newline at end of file diff --git a/tests/data/users/user_options.json b/tests/data/users/user_options.json new file mode 100644 index 00000000..1f1f0ce3 --- /dev/null +++ b/tests/data/users/user_options.json @@ -0,0 +1,69 @@ +{ + "id": "USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422", + "name": "my_user_name", + "image_url": "https://example.com/image.png", + "display_name": "My User Name", + "properties": { + "custom_data": { + "custom_key": "custom_value" + } + }, + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422" + } + }, + "channels": { + "pstn": [ + { + "number": 123457 + } + ], + "sip": [ + { + "uri": "sip:4442138907@sip.example.com;transport=tls", + "username": "New SIP", + "password": "Password" + } + ], + "vbc": [ + { + "extension": "403" + } + ], + "websocket": [ + { + "uri": "wss://example.com/socket", + "content-type": "audio/l16;rate=16000", + "headers": { + "customer_id": "ABC123" + } + } + ], + "sms": [ + { + "number": "447700900000" + } + ], + "mms": [ + { + "number": "447700900000" + } + ], + "whatsapp": [ + { + "number": "447700900000" + } + ], + "viber": [ + { + "number": "447700900000" + } + ], + "messenger": [ + { + "id": "12345abcd" + } + ] + } +} \ No newline at end of file diff --git a/tests/data/users/user_updated.json b/tests/data/users/user_updated.json new file mode 100644 index 00000000..be4b884d --- /dev/null +++ b/tests/data/users/user_updated.json @@ -0,0 +1,19 @@ +{ + "id": "USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5", + "name": "updated_name", + "properties": { + "custom_data": {} + }, + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5" + } + }, + "channels": { + "whatsapp": [ + { + "number": "447700900000" + } + ] + } +} \ No newline at end of file diff --git a/tests/test_users.py b/tests/test_users.py new file mode 100644 index 00000000..3a468548 --- /dev/null +++ b/tests/test_users.py @@ -0,0 +1,369 @@ +from vonage import Client, Users +from util import * +from vonage.errors import UsersError, ClientError, ServerError + +from pytest import raises +import responses + +client = Client() +users = Users(client) +host = client.api_host() + + +@responses.activate +def test_list_users_basic(): + stub( + responses.GET, + f'https://{host}/v1/users', + fixture_path='users/list_users_basic.json', + ) + + all_users = users.list_users() + assert all_users['page_size'] == 10 + assert all_users['_embedded']['users'][0]['name'] == 'NAM-6dd4ea1f-3841-47cb-a3d3-e271f5c1e33c' + assert all_users['_embedded']['users'][1]['name'] == 'NAM-ecb938f2-13e0-40c1-9d3b-b16ebb4ef3d1' + assert all_users['_embedded']['users'][2]['name'] == 'my_user_name' + + +@responses.activate +def test_list_users_options(): + stub( + responses.GET, + f'https://{host}/v1/users', + fixture_path='users/list_users_options.json', + ) + + all_users = users.list_users(page_size=2, order='desc') + assert all_users['page_size'] == 2 + assert all_users['_embedded']['users'][0]['name'] == 'my_user_name' + assert all_users['_embedded']['users'][1]['name'] == 'NAM-ecb938f2-13e0-40c1-9d3b-b16ebb4ef3d1' + + +def test_list_users_order_error(): + with raises(UsersError) as err: + users.list_users(order='Why, ascending of course!') + assert ( + str(err.value) == 'Invalid order parameter. Must be one of: "asc", "desc", "ASC", "DESC".' + ) + + +@responses.activate +def test_list_users_400(): + stub( + responses.GET, + f'https://{host}/v1/users', + fixture_path='users/list_users_400.json', + status_code=400, + ) + + with raises(ClientError) as err: + users.list_users(page_size='asdf') + assert 'Input validation failure.' in str(err.value) + + +@responses.activate +def test_list_users_404(): + stub( + responses.GET, + f'https://{host}/v1/users', + fixture_path='users/list_users_404.json', + status_code=404, + ) + + with raises(ClientError) as err: + users.list_users(name='asdf') + assert 'User does not exist, or you do not have access.' in str(err.value) + + +@responses.activate +def test_list_users_429(): + stub( + responses.GET, + f'https://{host}/v1/users', + fixture_path='users/rate_limit.json', + status_code=429, + ) + + with raises(ClientError) as err: + users.list_users() + assert 'You have exceeded your request limit. You can try again shortly.' in str(err.value) + + +@responses.activate +def test_list_users_500(): + stub( + responses.GET, + f'https://{host}/v1/users', + fixture_path='users/list_users_500.json', + status_code=500, + ) + + with raises(ServerError) as err: + users.list_users() + assert str(err.value) == '500 response from api.nexmo.com' + + +@responses.activate +def test_create_user_basic(): + stub( + responses.POST, + f'https://{host}/v1/users', + fixture_path='users/user_basic.json', + status_code=201, + ) + + user = users.create_user() + assert user['id'] == 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + assert user['name'] == 'NAM-ecb938f2-13e0-40c1-9d3b-b16ebb4ef3d1' + assert ( + user['_links']['self']['href'] + == 'https://api-us-3.vonage.com/v1/users/USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + ) + + +@responses.activate +def test_create_user_options(): + stub( + responses.POST, + f'https://{host}/v1/users', + fixture_path='users/user_options.json', + status_code=201, + ) + + params = { + "id": "USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422", + "name": "my_user_name", + "image_url": "https://example.com/image.png", + "display_name": "My User Name", + "properties": {"custom_data": {"custom_key": "custom_value"}}, + "_links": { + "self": { + "href": "https://api-us-3.vonage.com/v1/users/USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422" + } + }, + "channels": { + "pstn": [{"number": 123457}], + "sip": [ + { + "uri": "sip:4442138907@sip.example.com;transport=tls", + "username": "New SIP", + "password": "Password", + } + ], + "vbc": [{"extension": "403"}], + "websocket": [ + { + "uri": "wss://example.com/socket", + "content-type": "audio/l16;rate=16000", + "headers": {"customer_id": "ABC123"}, + } + ], + "sms": [{"number": "447700900000"}], + "mms": [{"number": "447700900000"}], + "whatsapp": [{"number": "447700900000"}], + "viber": [{"number": "447700900000"}], + "messenger": [{"id": "12345abcd"}], + }, + } + + user = users.create_user(params) + assert user['id'] == 'USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422' + assert user['name'] == 'my_user_name' + assert user['display_name'] == 'My User Name' + assert user['properties']['custom_data']['custom_key'] == 'custom_value' + assert ( + user['_links']['self']['href'] + == 'https://api-us-3.vonage.com/v1/users/USR-5ab17d58-b8b3-427d-ac42-c31dab7ef422' + ) + assert user['channels']['vbc'][0]['extension'] == '403' + + +@responses.activate +def test_create_user_400(): + stub( + responses.POST, + f'https://{host}/v1/users', + fixture_path='users/user_400.json', + status_code=400, + ) + + with raises(ClientError) as err: + users.create_user(params={'name': 1234}) + assert 'Input validation failure.' in str(err.value) + + +@responses.activate +def test_create_user_429(): + stub( + responses.POST, + f'https://{host}/v1/users', + fixture_path='users/rate_limit.json', + status_code=429, + ) + + with raises(ClientError) as err: + users.create_user() + assert 'You have exceeded your request limit. You can try again shortly.' in str(err.value) + + +@responses.activate +def test_get_user(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.GET, + f'https://{host}/v1/users/{user_id}', + fixture_path='users/user_basic.json', + ) + + user = users.get_user(user_id) + assert user['name'] == 'NAM-ecb938f2-13e0-40c1-9d3b-b16ebb4ef3d1' + assert user['properties']['custom_data'] == {} + assert ( + user['_links']['self']['href'] + == 'https://api-us-3.vonage.com/v1/users/USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + ) + + +@responses.activate +def test_get_user_404(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.GET, + f'https://{host}/v1/users/{user_id}', + status_code=404, + fixture_path='users/user_404.json', + ) + + with raises(ClientError) as err: + users.get_user(user_id) + assert 'User does not exist, or you do not have access.' in str(err.value) + + +@responses.activate +def test_get_user_429(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.GET, + f'https://{host}/v1/users/{user_id}', + fixture_path='users/rate_limit.json', + status_code=429, + ) + + with raises(ClientError) as err: + users.get_user(user_id) + assert 'You have exceeded your request limit. You can try again shortly.' in str(err.value) + + +@responses.activate +def test_update_user(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.PATCH, + f'https://{host}/v1/users/{user_id}', + fixture_path='users/user_updated.json', + ) + + params = { + 'name': 'updated_name', + 'channels': { + 'whatsapp': [ + {'number': '447700900000'}, + ] + }, + } + user = users.update_user(user_id, params) + assert user['name'] == 'updated_name' + assert ( + user['_links']['self']['href'] + == 'https://api-us-3.vonage.com/v1/users/USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + ) + assert user['channels']['whatsapp'][0]['number'] == '447700900000' + + +@responses.activate +def test_update_user_400(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.PATCH, + f'https://{host}/v1/users/{user_id}', + fixture_path='users/user_400.json', + status_code=400, + ) + + with raises(ClientError) as err: + users.update_user(user_id, params={'name': 1234}) + assert 'Input validation failure.' in str(err.value) + + +@responses.activate +def test_update_user_404(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.PATCH, + f'https://{host}/v1/users/{user_id}', + status_code=404, + fixture_path='users/user_404.json', + ) + + with raises(ClientError) as err: + users.update_user(user_id, params={'name': 'updated_user_name'}) + assert 'User does not exist, or you do not have access.' in str(err.value) + + +@responses.activate +def test_update_user_429(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.PATCH, + f'https://{host}/v1/users/{user_id}', + fixture_path='users/rate_limit.json', + status_code=429, + ) + + with raises(ClientError) as err: + users.update_user(user_id, params={'name': 'updated_user_name'}) + assert 'You have exceeded your request limit. You can try again shortly.' in str(err.value) + + +@responses.activate +def test_delete_user(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.DELETE, + f'https://{host}/v1/users/{user_id}', + status_code=204, + fixture_path='no_content.json', + ) + + response = users.delete_user(user_id) + assert response == None + + +@responses.activate +def test_delete_user_404(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.DELETE, + f'https://{host}/v1/users/{user_id}', + status_code=404, + fixture_path='users/user_404.json', + ) + + with raises(ClientError) as err: + users.delete_user(user_id) + assert 'User does not exist, or you do not have access.' in str(err.value) + + +@responses.activate +def test_delete_user_429(): + user_id = 'USR-d3cc6a55-aa7b-4916-8244-2fedb554afd5' + stub( + responses.DELETE, + f'https://{host}/v1/users/{user_id}', + fixture_path='users/rate_limit.json', + status_code=429, + ) + + with raises(ClientError) as err: + users.delete_user(user_id) + assert 'You have exceeded your request limit. You can try again shortly.' in str(err.value)