diff --git a/CHANGELOG.md b/CHANGELOG.md index b3bc619d..625e5549 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,17 @@ # Change Log +## [4.61.0](https://github.com/plivo/plivo-python/tree/v4.61.0) (2026-05-23) +**Feature - whatsapp_templates, verify_apps, verify_app_templates API updates** +- Added `application_id` optional parameter to whatsapp_templates.create (POST /v1/Account/{auth_id}/WhatsApp/Template/{waba_id}/) +- Added `application_id` optional parameter to whatsapp_templates.update (POST /v1/Account/{auth_id}/WhatsApp/Template/{waba_id}/{template_id}/) +- Added `name`, `otp_type`, `otp_length`, `otp_expiry`, `otp_attempts`, `brand_name`, `sms_channel`, `voice_channel`, `wa_channel`, `is_default`, `template_uuid`, `message_redaction`, `customer_app_hash`, `max_validation_attempts`, `enable_fraudshield`, `fs_protection_level`, `waba_id`, `waba_phone_number`, `waba_template_id` parameters to verify_apps.create (POST /v1/Account/{auth_id}/Verify/App/) +- Added `name`, `app_uuid`, `channel`, `status`, `limit`, `offset`, `created_at`, `created_at__lt`, `created_at__lte`, `created_at__gt`, `created_at__gte`, `subaccount_auth_id` parameters to verify_apps.list (GET /v1/Account/{auth_id}/Verify/App/) +- GET /v1/Account/{auth_id}/Verify/App/templates/ — verify_app_templates.list. List available SMS OTP templates for the account including account-owned and Plivo system defaults. +- GET /v1/Account/{auth_id}/Verify/App/{app_uuid}/ — verify_apps.get. Retrieve details of a specific Verify App by its UUID. +- Added `name`, `brand_name`, `otp_type`, `otp_length`, `otp_expiry`, `otp_attempts`, `sms_channel`, `voice_channel`, `wa_channel`, `is_default`, `template_uuid`, `message_redaction`, `customer_app_hash`, `max_validation_attempts`, `enable_fraudshield`, `fs_protection_level`, `waba_id`, `waba_phone_number`, `waba_template_id` parameters to verify_apps.update (POST /v1/Account/{auth_id}/Verify/App/{app_uuid}/) +- DELETE /v1/Account/{auth_id}/Verify/App/{app_uuid}/ — verify_apps.delete. Soft-delete a Verify App by its UUID. + +_Source: plivo/api-messaging#629_ + ## [4.60.1](https://github.com/plivo/plivo-python/tree/v4.60.1) (2026-04-17) **Bug Fix - PhoneNumber Compliance API** - Fixed Requirements.get() sending None values as query params when not provided diff --git a/examples/verify_app_templates_list.py b/examples/verify_app_templates_list.py new file mode 100644 index 00000000..9261b94c --- /dev/null +++ b/examples/verify_app_templates_list.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +import plivo + +client = plivo.RestClient(auth_id='YOUR_AUTH_ID', auth_token='YOUR_AUTH_TOKEN') + +response = client.verify_app_templates.list() + +print('API ID:', response.api_id) +print('Templates:') +for template in response: + print(' -', template) \ No newline at end of file diff --git a/examples/verify_apps_create.py b/examples/verify_apps_create.py new file mode 100644 index 00000000..a111a89d --- /dev/null +++ b/examples/verify_apps_create.py @@ -0,0 +1,21 @@ +import plivo + +client = plivo.RestClient(auth_id='YOUR_AUTH_ID', auth_token='YOUR_AUTH_TOKEN') + +response = client.verify_apps.create( + name='MyVerifyApp', + otp_length=6, + otp_expiry=3, + otp_attempts=3, + brand_name='MyBrand', + sms_channel=True, + voice_channel=False, + wa_channel=False, + is_default=False, + message_redaction=False, + max_validation_attempts=5, + enable_fraudshield=True, + fs_protection_level='medium', +) + +print(response) \ No newline at end of file diff --git a/examples/verify_apps_delete.py b/examples/verify_apps_delete.py new file mode 100644 index 00000000..c922a01d --- /dev/null +++ b/examples/verify_apps_delete.py @@ -0,0 +1,9 @@ +import plivo + +client = plivo.RestClient(auth_id='YOUR_AUTH_ID', auth_token='YOUR_AUTH_TOKEN') + +app_uuid = 'your-app-uuid' + +response = client.verify_apps.delete(app_uuid) + +print(response) \ No newline at end of file diff --git a/examples/verify_apps_get.py b/examples/verify_apps_get.py new file mode 100644 index 00000000..7e0ab2b1 --- /dev/null +++ b/examples/verify_apps_get.py @@ -0,0 +1,9 @@ +import plivo + +client = plivo.RestClient(auth_id='YOUR_AUTH_ID', auth_token='YOUR_AUTH_TOKEN') + +app_uuid = 'your-app-uuid' + +response = client.verify_apps.get(app_uuid) + +print(response) \ No newline at end of file diff --git a/examples/verify_apps_list.py b/examples/verify_apps_list.py new file mode 100644 index 00000000..96943e77 --- /dev/null +++ b/examples/verify_apps_list.py @@ -0,0 +1,11 @@ +import plivo + +client = plivo.RestClient(auth_id='YOUR_AUTH_ID', auth_token='YOUR_AUTH_TOKEN') + +response = client.verify_apps.list( + limit=20, + offset=0, + status='active', +) + +print(response) \ No newline at end of file diff --git a/examples/verify_apps_update.py b/examples/verify_apps_update.py new file mode 100644 index 00000000..76b3d6b5 --- /dev/null +++ b/examples/verify_apps_update.py @@ -0,0 +1,15 @@ +import plivo + +client = plivo.RestClient(auth_id='YOUR_AUTH_ID', auth_token='YOUR_AUTH_TOKEN') + +app_uuid = 'your-app-uuid' + +response = client.verify_apps.update( + app_uuid, + name='UpdatedAppName', + otp_length=8, + enable_fraudshield=True, + fs_protection_level='high', +) + +print(response) \ No newline at end of file diff --git a/examples/whatsapp_templates_create.py b/examples/whatsapp_templates_create.py new file mode 100644 index 00000000..4c712ba6 --- /dev/null +++ b/examples/whatsapp_templates_create.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +import plivo + +client = plivo.RestClient(auth_id='YOUR_AUTH_ID', auth_token='YOUR_AUTH_TOKEN') + +# Create a WhatsApp template without application_id +response = client.whatsapp_templates.create( + waba_id='your_waba_id', + name='your_template_name', + language='en_US', + category='MARKETING', + components=[ + { + 'type': 'BODY', + 'text': 'Hello, this is a template message.', + } + ], +) +print(response) + +# Create a WhatsApp template with optional application_id +response = client.whatsapp_templates.create( + waba_id='your_waba_id', + name='your_template_name', + language='en_US', + category='MARKETING', + components=[ + { + 'type': 'BODY', + 'text': 'Hello, this is a template message.', + } + ], + application_id='your_application_id', +) +print(response) \ No newline at end of file diff --git a/examples/whatsapp_templates_update.py b/examples/whatsapp_templates_update.py new file mode 100644 index 00000000..ba4fdd75 --- /dev/null +++ b/examples/whatsapp_templates_update.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +import plivo + +client = plivo.RestClient(auth_id='YOUR_AUTH_ID', auth_token='YOUR_AUTH_TOKEN') + +# Update a WhatsApp template without application_id +response = client.whatsapp_templates.update( + waba_id='your_waba_id', + template_id='your_template_id', + components=[ + { + 'type': 'BODY', + 'text': 'Hello, this is an updated template message.', + } + ], +) +print(response) + +# Update a WhatsApp template with optional application_id +response = client.whatsapp_templates.update( + waba_id='your_waba_id', + template_id='your_template_id', + components=[ + { + 'type': 'BODY', + 'text': 'Hello, this is an updated template message.', + } + ], + application_id='your_application_id', +) +print(response) \ No newline at end of file diff --git a/plivo/resources/verify_app_templates.py b/plivo/resources/verify_app_templates.py new file mode 100644 index 00000000..a38d7963 --- /dev/null +++ b/plivo/resources/verify_app_templates.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +from plivo.base import PlivoResource, PlivoResourceInterface, ResponseObject +from plivo.utils import to_param_dict +from plivo.utils.validators import * + + +class VerifyAppTemplate(ResponseObject): + def __init__(self, dct): + super(VerifyAppTemplate, self).__init__(dct) + + +class VerifyAppTemplatesResponse(ResponseObject): + def __init__(self, client, dct): + super(VerifyAppTemplatesResponse, self).__init__(dct) + self.api_id = dct.get('api_id', None) + self.templates = dct.get('templates', []) + + def __iter__(self): + if self.templates is not None: + return iter(self.templates) + return iter([]) + + def __len__(self): + if self.templates is not None: + return len(self.templates) + return 0 + + def __str__(self): + import pprint + return pprint.pformat({'api_id': self.api_id, 'templates': self.templates}) + + def __repr__(self): + return self.__str__() + + +class VerifyAppTemplates(PlivoResourceInterface): + def list(self): + return self.client.request( + 'GET', + ('Verify', 'App', 'templates'), + response_type=VerifyAppTemplatesResponse, + ) \ No newline at end of file diff --git a/plivo/resources/verify_apps.py b/plivo/resources/verify_apps.py new file mode 100644 index 00000000..ad3d1b02 --- /dev/null +++ b/plivo/resources/verify_apps.py @@ -0,0 +1,247 @@ +# -*- coding: utf-8 -*- +from plivo.utils.validators import * + +from ..base import PlivoResource, PlivoResourceInterface, ResponseObject +from ..exceptions import * +from ..utils import * + + +class VerifyApp(PlivoResource): + _name = 'VerifyApp' + _identifier_string = 'app_uuid' + + def get(self): + return self.client.verify_apps.get(self.id) + + def update(self, + name=None, + brand_name=None, + otp_type=None, + otp_length=None, + otp_expiry=None, + otp_attempts=None, + sms_channel=None, + voice_channel=None, + wa_channel=None, + is_default=None, + template_uuid=None, + message_redaction=None, + customer_app_hash=None, + max_validation_attempts=None, + enable_fraudshield=None, + fs_protection_level=None, + waba_id=None, + waba_phone_number=None, + waba_template_id=None): + return self.client.verify_apps.update( + self.id, + name=name, + brand_name=brand_name, + otp_type=otp_type, + otp_length=otp_length, + otp_expiry=otp_expiry, + otp_attempts=otp_attempts, + sms_channel=sms_channel, + voice_channel=voice_channel, + wa_channel=wa_channel, + is_default=is_default, + template_uuid=template_uuid, + message_redaction=message_redaction, + customer_app_hash=customer_app_hash, + max_validation_attempts=max_validation_attempts, + enable_fraudshield=enable_fraudshield, + fs_protection_level=fs_protection_level, + waba_id=waba_id, + waba_phone_number=waba_phone_number, + waba_template_id=waba_template_id, + ) + + def delete(self): + return self.client.verify_apps.delete(self.id) + + +class ListVerifyAppsResponse(ResponseObject): + def __init__(self, client, dct): + super(ListVerifyAppsResponse, self).__init__(dct) + self.error = dct.get('error', None) + self.verify_apps = dct.get('verify_apps', None) + self.meta = dct.get('meta', None) + self.apiID = dct.get('api_id', None) + + def __iter__(self): + if self.verify_apps is not None: + return self.verify_apps.__iter__() + return iter([]) + + def __len__(self): + if self.verify_apps is not None: + return len(self.verify_apps) + return 0 + + def __str__(self): + import pprint + if self.verify_apps is not None: + response_dict = { + 'api_id': self.apiID, + 'meta': self.meta, + 'verify_apps': self.verify_apps, + } + return pprint.pformat(response_dict) + return str(self.error) + + def __repr__(self): + if self.verify_apps is not None: + response_dict = { + 'api_id': self.apiID, + 'meta': self.meta, + 'verify_apps': [app for app in self.verify_apps], + } + return str(response_dict) + return str(self.error) + + def has_error(self): + return self.error is not None + + +class VerifyApps(PlivoResourceInterface): + _resource_type = VerifyApp + + @validate_args( + name=[of_type(six.text_type)], + otp_type=[optional(of_type(six.text_type))], + otp_length=[optional(of_type(*six.integer_types))], + otp_expiry=[optional(of_type(*six.integer_types))], + otp_attempts=[optional(of_type(*six.integer_types))], + brand_name=[optional(of_type(six.text_type))], + sms_channel=[optional(of_type_exact(bool))], + voice_channel=[optional(of_type_exact(bool))], + wa_channel=[optional(of_type_exact(bool))], + is_default=[optional(of_type_exact(bool))], + template_uuid=[optional(of_type(six.text_type))], + message_redaction=[optional(of_type_exact(bool))], + customer_app_hash=[optional(of_type(six.text_type))], + max_validation_attempts=[optional(of_type(*six.integer_types))], + enable_fraudshield=[optional(of_type_exact(bool))], + fs_protection_level=[optional(of_type(six.text_type))], + waba_id=[optional(of_type(six.text_type))], + waba_phone_number=[optional(of_type(six.text_type))], + waba_template_id=[optional(of_type(six.text_type))], + ) + def create(self, + name, + otp_type=None, + otp_length=None, + otp_expiry=None, + otp_attempts=None, + brand_name=None, + sms_channel=None, + voice_channel=None, + wa_channel=None, + is_default=None, + template_uuid=None, + message_redaction=None, + customer_app_hash=None, + max_validation_attempts=None, + enable_fraudshield=None, + fs_protection_level=None, + waba_id=None, + waba_phone_number=None, + waba_template_id=None): + return self.client.request( + 'POST', ('Verify', 'App'), + to_param_dict(self.create, locals())) + + @validate_args( + name=[optional(of_type(six.text_type))], + app_uuid=[optional(of_type(six.text_type))], + channel=[optional(of_type(six.text_type))], + status=[optional(of_type(six.text_type))], + limit=[optional(of_type(*six.integer_types))], + offset=[optional(of_type(*six.integer_types))], + created_at=[optional(of_type(six.text_type))], + created_at__lt=[optional(of_type(six.text_type))], + created_at__lte=[optional(of_type(six.text_type))], + created_at__gt=[optional(of_type(six.text_type))], + created_at__gte=[optional(of_type(six.text_type))], + subaccount_auth_id=[optional(of_type(six.text_type))], + ) + def list(self, + name=None, + app_uuid=None, + channel=None, + status=None, + limit=None, + offset=None, + created_at=None, + created_at__lt=None, + created_at__lte=None, + created_at__gt=None, + created_at__gte=None, + subaccount_auth_id=None): + return self.client.request( + 'GET', ('Verify', 'App'), + to_param_dict(self.list, locals()), + response_type=ListVerifyAppsResponse, + objects_type=VerifyApp) + + @validate_args( + app_uuid=[of_type(six.text_type)], + ) + def get(self, app_uuid): + return self.client.request( + 'GET', ('Verify', 'App', app_uuid), + response_type=VerifyApp) + + @validate_args( + app_uuid=[of_type(six.text_type)], + name=[optional(of_type(six.text_type))], + brand_name=[optional(of_type(six.text_type))], + otp_type=[optional(of_type(six.text_type))], + otp_length=[optional(of_type(*six.integer_types))], + otp_expiry=[optional(of_type(*six.integer_types))], + otp_attempts=[optional(of_type(*six.integer_types))], + sms_channel=[optional(of_type_exact(bool))], + voice_channel=[optional(of_type_exact(bool))], + wa_channel=[optional(of_type_exact(bool))], + is_default=[optional(of_type_exact(bool))], + template_uuid=[optional(of_type(six.text_type))], + message_redaction=[optional(of_type_exact(bool))], + customer_app_hash=[optional(of_type(six.text_type))], + max_validation_attempts=[optional(of_type(*six.integer_types))], + enable_fraudshield=[optional(of_type_exact(bool))], + fs_protection_level=[optional(of_type(six.text_type))], + waba_id=[optional(of_type(six.text_type))], + waba_phone_number=[optional(of_type(six.text_type))], + waba_template_id=[optional(of_type(six.text_type))], + ) + def update(self, + app_uuid, + name=None, + brand_name=None, + otp_type=None, + otp_length=None, + otp_expiry=None, + otp_attempts=None, + sms_channel=None, + voice_channel=None, + wa_channel=None, + is_default=None, + template_uuid=None, + message_redaction=None, + customer_app_hash=None, + max_validation_attempts=None, + enable_fraudshield=None, + fs_protection_level=None, + waba_id=None, + waba_phone_number=None, + waba_template_id=None): + return self.client.request( + 'POST', ('Verify', 'App', app_uuid), + to_param_dict(self.update, locals())) + + @validate_args( + app_uuid=[of_type(six.text_type)], + ) + def delete(self, app_uuid): + return self.client.request( + 'DELETE', ('Verify', 'App', app_uuid)) \ No newline at end of file diff --git a/plivo/resources/whatsapp_templates.py b/plivo/resources/whatsapp_templates.py new file mode 100644 index 00000000..50c8d339 --- /dev/null +++ b/plivo/resources/whatsapp_templates.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +from plivo.utils.validators import * + +from ..base import PlivoResource, PlivoResourceInterface +from ..exceptions import * +from ..utils import * + + +class WhatsappTemplate(PlivoResource): + _name = 'WhatsappTemplate' + _identifier_string = 'template_id' + + def delete(self): + return self.client.whatsapp_templates.delete( + self.__dict__.get('waba_id'), self.id) + + def update(self, + components=None, + application_id=None): + return self.client.whatsapp_templates.update( + self.__dict__.get('waba_id'), + self.id, + components=components, + application_id=application_id) + + +class WhatsappTemplates(PlivoResourceInterface): + _resource_type = WhatsappTemplate + + @validate_args( + waba_id=[of_type(six.text_type)], + name=[of_type(six.text_type)], + language=[of_type(six.text_type)], + category=[of_type(six.text_type)], + components=[optional(of_type_exact(list))], + application_id=[optional(of_type(six.text_type))], + ) + def create(self, + waba_id, + name, + language, + category, + components=None, + application_id=None): + return self.client.request( + 'POST', + ('WhatsApp', 'Template', waba_id), + to_param_dict(self.create, locals())) + + @validate_args( + waba_id=[of_type(six.text_type)], + template_id=[of_type(six.text_type)], + components=[optional(of_type_exact(list))], + application_id=[optional(of_type(six.text_type))], + ) + def update(self, + waba_id, + template_id, + components=None, + application_id=None): + return self.client.request( + 'POST', + ('WhatsApp', 'Template', waba_id, template_id), + to_param_dict(self.update, locals())) + + @validate_args( + waba_id=[of_type(six.text_type)], + template_id=[of_type(six.text_type)], + ) + def delete(self, waba_id, template_id): + return self.client.request( + 'DELETE', + ('WhatsApp', 'Template', waba_id, template_id)) + + @validate_args( + waba_id=[of_type(six.text_type)], + template_id=[of_type(six.text_type)], + ) + def get(self, waba_id, template_id): + return self.client.request( + 'GET', + ('WhatsApp', 'Template', waba_id, template_id), + response_type=WhatsappTemplate) + + @validate_args( + waba_id=[of_type(six.text_type)], + ) + def list(self, + waba_id, + limit=None, + offset=None): + return self.client.request( + 'GET', + ('WhatsApp', 'Template', waba_id), + to_param_dict(self.list, locals()), + response_type=WhatsappTemplate) \ No newline at end of file diff --git a/setup.py b/setup.py index 5aa4ad1e..18f23075 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup( name='plivo', - version='4.60.1', + version='4.61.0', description='A Python SDK to make voice calls & send SMS using Plivo and to generate Plivo XML', long_description=long_description, url='https://github.com/plivo/plivo-python', diff --git a/tests/resources/test_verify_app_templates.py b/tests/resources/test_verify_app_templates.py new file mode 100644 index 00000000..a6ae10c7 --- /dev/null +++ b/tests/resources/test_verify_app_templates.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from tests.base import PlivoResourceTestCase + + +class VerifyAppTemplatesTest(PlivoResourceTestCase): + def test_list(self): + expected_response = { + 'api_id': 'some-api-id', + 'templates': [ + { + 'template_uuid': 'uuid-1', + 'text': 'Your OTP is {{otp}}', + 'friendly_name': 'Default OTP', + 'locale': 'en', + }, + { + 'template_uuid': 'uuid-2', + 'text': 'Use {{otp}} to verify', + 'friendly_name': 'Verify OTP', + 'locale': 'en', + }, + ], + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + response = self.client.verify_app_templates.list() + + self.assertEqual( + self.client.current_request.url, + 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verify/App/templates/') + self.assertEqual(self.client.current_request.method, 'GET') + self.assertEqual(response.api_id, expected_response['api_id']) + self.assertEqual(len(response), 2) + templates = list(response) + self.assertEqual(templates[0]['template_uuid'], 'uuid-1') + self.assertEqual(templates[1]['template_uuid'], 'uuid-2') \ No newline at end of file diff --git a/tests/resources/test_verify_apps.py b/tests/resources/test_verify_apps.py new file mode 100644 index 00000000..89a8a400 --- /dev/null +++ b/tests/resources/test_verify_apps.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +from tests.base import PlivoResourceTestCase +from tests.decorators import with_response + + +class VerifyAppTest(PlivoResourceTestCase): + + def test_create_verify_app(self): + expected_response = { + 'api_id': 'test-api-id', + 'app_uuid': 'test-app-uuid', + 'message': 'Verify app created successfully.', + } + self.client.set_expected_response( + status_code=201, data_to_return=expected_response) + + response = self.client.verify_apps.create(name='TestApp') + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('Verify', 'App')) + self.assertEqual(response.app_uuid, expected_response['app_uuid']) + self.assertEqual(response.message, expected_response['message']) + + def test_create_verify_app_with_all_params(self): + expected_response = { + 'api_id': 'test-api-id', + 'app_uuid': 'test-app-uuid', + 'message': 'Verify app created successfully.', + } + self.client.set_expected_response( + status_code=201, data_to_return=expected_response) + + response = self.client.verify_apps.create( + name='TestApp', + otp_type='integer', + otp_length=6, + otp_expiry=3, + otp_attempts=3, + brand_name='MyBrand', + sms_channel=True, + voice_channel=False, + wa_channel=False, + is_default=False, + template_uuid='template-uuid', + message_redaction=False, + customer_app_hash='abcde12345a', + max_validation_attempts=5, + enable_fraudshield=True, + fs_protection_level='medium', + ) + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertEqual(response.app_uuid, expected_response['app_uuid']) + + def test_create_verify_app_with_wa_channel(self): + expected_response = { + 'api_id': 'test-api-id', + 'app_uuid': 'test-app-uuid-wa', + 'message': 'Verify app created successfully.', + } + self.client.set_expected_response( + status_code=201, data_to_return=expected_response) + + response = self.client.verify_apps.create( + name='WAApp', + wa_channel=True, + waba_id='waba-id-123', + waba_phone_number='+14155551234', + waba_template_id='meta-template-id', + ) + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertEqual(response.app_uuid, expected_response['app_uuid']) + + def test_list_verify_apps(self): + expected_response = { + 'api_id': 'test-api-id', + 'meta': { + 'limit': 20, + 'offset': 0, + 'total_count': 1, + 'next': None, + 'previous': None, + }, + 'verify_apps': [ + {'app_uuid': 'test-app-uuid', 'name': 'TestApp'}, + ], + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + response = self.client.verify_apps.list() + + self.assertEqual(self.client.current_request.method, 'GET') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('Verify', 'App')) + self.assertEqual(len(list(response)), 1) + + def test_list_verify_apps_with_filters(self): + expected_response = { + 'api_id': 'test-api-id', + 'meta': { + 'limit': 5, + 'offset': 0, + 'total_count': 1, + 'next': None, + 'previous': None, + }, + 'verify_apps': [ + {'app_uuid': 'test-app-uuid', 'name': 'TestApp'}, + ], + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + response = self.client.verify_apps.list( + name='TestApp', + channel='sms', + status='active', + limit=5, + offset=0, + ) + + self.assertEqual(self.client.current_request.method, 'GET') + self.assertEqual(len(list(response)), 1) + + def test_get_verify_app(self): + app_uuid = 'test-app-uuid' + expected_response = { + 'api_id': 'test-api-id', + 'verify_app': { + 'app_uuid': app_uuid, + 'name': 'TestApp', + }, + 'verify_whatsapp': None, + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + response = self.client.verify_apps.get(app_uuid) + + self.assertEqual(self.client.current_request.method, 'GET') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('Verify', 'App', app_uuid)) + + def test_update_verify_app(self): + app_uuid = 'test-app-uuid' + expected_response = { + 'api_id': 'test-api-id', + 'app_uuid': app_uuid, + 'message': 'Verify app updated successfully.', + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + response = self.client.verify_apps.update( + app_uuid, + name='UpdatedApp', + otp_length=8, + ) + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('Verify', 'App', app_uuid)) + self.assertEqual(response.app_uuid, expected_response['app_uuid']) + + def test_update_verify_app_with_wa_channel(self): + app_uuid = 'test-app-uuid' + expected_response = { + 'api_id': 'test-api-id', + 'app_uuid': app_uuid, + 'message': 'Verify app updated successfully.', + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + response = self.client.verify_apps.update( + app_uuid, + wa_channel=True, + waba_id='waba-id-456', + waba_phone_number='+14155559999', + waba_template_id='meta-template-id-2', + ) + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertEqual(response.app_uuid, expected_response['app_uuid']) + + def test_delete_verify_app(self): + app_uuid = 'test-app-uuid' + expected_response = { + 'api_id': 'test-api-id', + 'app_uuid': app_uuid, + 'message': 'Verify app deleted successfully.', + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + self.client.verify_apps.delete(app_uuid) + + self.assertEqual(self.client.current_request.method, 'DELETE') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('Verify', 'App', app_uuid)) \ No newline at end of file diff --git a/tests/resources/test_whatsapp_templates.py b/tests/resources/test_whatsapp_templates.py new file mode 100644 index 00000000..3f867bc5 --- /dev/null +++ b/tests/resources/test_whatsapp_templates.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +from tests.base import PlivoResourceTestCase + + +class WhatsappTemplateTest(PlivoResourceTestCase): + + def test_create_template(self): + expected_response = { + 'api_id': 'api-id-string', + 'template_id': 'template-uuid-string', + } + self.client.set_expected_response( + status_code=201, data_to_return=expected_response) + + response = self.client.whatsapp_templates.create( + waba_id='waba-id-string', + name='test_template', + language='en_US', + category='MARKETING', + components=[{'type': 'BODY', 'text': 'Hello'}]) + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('WhatsApp', 'Template', 'waba-id-string')) + + def test_create_template_with_application_id(self): + expected_response = { + 'api_id': 'api-id-string', + 'template_id': 'template-uuid-string', + } + self.client.set_expected_response( + status_code=201, data_to_return=expected_response) + + response = self.client.whatsapp_templates.create( + waba_id='waba-id-string', + name='test_template', + language='en_US', + category='MARKETING', + components=[{'type': 'BODY', 'text': 'Hello'}], + application_id='app-id-string') + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('WhatsApp', 'Template', 'waba-id-string')) + + def test_update_template(self): + expected_response = { + 'api_id': 'api-id-string', + 'template_id': 'template-uuid-string', + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + response = self.client.whatsapp_templates.update( + waba_id='waba-id-string', + template_id='template-uuid-string', + components=[{'type': 'BODY', 'text': 'Hello Updated'}]) + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('WhatsApp', 'Template', 'waba-id-string', + 'template-uuid-string')) + + def test_update_template_with_application_id(self): + expected_response = { + 'api_id': 'api-id-string', + 'template_id': 'template-uuid-string', + } + self.client.set_expected_response( + status_code=200, data_to_return=expected_response) + + response = self.client.whatsapp_templates.update( + waba_id='waba-id-string', + template_id='template-uuid-string', + components=[{'type': 'BODY', 'text': 'Hello Updated'}], + application_id='app-id-string') + + self.assertEqual(self.client.current_request.method, 'POST') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('WhatsApp', 'Template', 'waba-id-string', + 'template-uuid-string')) + + def test_delete_template(self): + self.client.set_expected_response(status_code=204, data_to_return={}) + + self.client.whatsapp_templates.delete( + waba_id='waba-id-string', + template_id='template-uuid-string') + + self.assertEqual(self.client.current_request.method, 'DELETE') + self.assertUrlEqual( + self.client.current_request.url, + self.get_url('WhatsApp', 'Template', 'waba-id-string', + 'template-uuid-string')) \ No newline at end of file