Skip to content

Commit 1104d73

Browse files
DEVP-12 :: list & delete voice calls (#1)
* ignore venv folder * add listing for calls * fix list command comments * add delete call functionality * simplify string test
1 parent 2c8ba9d commit 1104d73

File tree

7 files changed

+259
-3
lines changed

7 files changed

+259
-3
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,6 @@ target/
5959

6060
# PyCharm
6161
.idea/
62+
63+
# pyvenv
64+
venv/

examples/call_delete.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
import json
5+
import argparse
6+
import requests
7+
8+
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
9+
import messagebird
10+
11+
parser = argparse.ArgumentParser(usage='call_create.py\
12+
--accessKey="*******" \
13+
--callId=dda20377-72da-4846-9b2c-0fea3ad4bcb6 \
14+
')
15+
parser.add_argument('--accessKey', help='Access key for MessageBird API.', type=str, required=True)
16+
parser.add_argument('--callId', help='The ID of the MessageBird call to delete.', type=str, required=True)
17+
args = vars(parser.parse_args())
18+
19+
try:
20+
# Create a MessageBird client with the specified accessKey.
21+
client = messagebird.Client(args['accessKey'])
22+
23+
# Create a call for the specified callID.
24+
call = client.call_delete(args['callId'])
25+
26+
# If no error is thrown, means delete was successful.
27+
print('\nDeleted call with id `%s` successfully!' % args['callId'])
28+
29+
except messagebird.client.ErrorException as e:
30+
print('\nAn error occurred while creating a call:\n')
31+
32+
for error in e.errors:
33+
print(' code : %d' % error.code)
34+
print(' description : %s' % error.description)
35+
print(' parameter : %s' % error.parameter)
36+
print(' type : %s' % error.__class__)
37+
38+
except requests.exceptions.HTTPError as e:
39+
print('\nAn http exception occurred while deleting a call:')
40+
print(' ', e)
41+
print(' Http request body: ', e.request.body)
42+
print(' Http response status: ', e.response.status_code)
43+
print(' Http response body: ', e.response.content.decode())
44+
45+
except Exception as e:
46+
print('\nAn ', e.__class__, ' exception occurred while deleting a call:')
47+
print(e)
48+

examples/call_list.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
import argparse
5+
import requests
6+
7+
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
8+
import messagebird
9+
10+
11+
parser = argparse.ArgumentParser(usage='call_create.py\
12+
--accessKey="*******" \
13+
--page=1 \
14+
')
15+
parser.add_argument('--accessKey', help='Access key for MessageBird API.', type=str, required=True)
16+
parser.add_argument('--page', help='The page you wish to view.', type=str, required=False, default=1)
17+
args = vars(parser.parse_args())
18+
19+
try:
20+
# Create a MessageBird client with the specified accessKey.
21+
client = messagebird.Client(args['accessKey'])
22+
del(args['accessKey'])
23+
24+
# Create a call for the specified callID.
25+
callList = client.call_list(**args)
26+
27+
# Print the object information.
28+
print('\nThe following information was returned as a %s object:\n' % callList.__class__)
29+
if callList.items is not None:
30+
print(' Containing the the following items:')
31+
for item in callList.items:
32+
print(' {')
33+
print(' id : %s' % item.id)
34+
print(' status : %s' % item.status)
35+
print(' source : %s' % item.source)
36+
print(' destination : %s' % item.destination)
37+
print(' createdAt : %s' % item.createdAt)
38+
print(' updatedAt : %s' % item.updatedAt)
39+
print(' endedAt : %s' % item.endedAt)
40+
print(' },')
41+
else:
42+
print(' With an empty response.')
43+
44+
except messagebird.client.ErrorException as e:
45+
print('\nAn error occurred while listing calls:\n')
46+
47+
for error in e.errors:
48+
print(' code : %d' % error.code)
49+
print(' description : %s' % error.description)
50+
print(' parameter : %s' % error.parameter)
51+
print(' type : %s' % error.__class__)
52+
53+
except requests.exceptions.HTTPError as e:
54+
print('\nAn HTTP exception occurred while listing calls:')
55+
print(' ', e)
56+
print(' Http request body: ', e.request.body)
57+
print(' Http response status: ', e.response.status_code)
58+
print(' Http response body: ', e.response.content.decode())
59+
60+
except Exception as e:
61+
print('\nAn ', e.__class__, ' exception occurred while creating a call:')
62+
print(e)
63+
64+

messagebird/base_list.py

+4
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ def items(self, value):
4343
items.append(self.itemType().load(item))
4444

4545
self._items = items
46+
47+
def __str__(self):
48+
items_count = 0 if self.items is None else len(self.items)
49+
return str(self.__class__) + " with %d items.\n" % items_count

messagebird/call_list.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from messagebird.base_list import BaseList
2+
from messagebird.call_data import CallData
3+
4+
5+
class CallList(BaseList):
6+
def __init__(self):
7+
# We're expecting items of type CallData
8+
super(CallList, self).__init__(CallData)
9+
self.perPage = None
10+
self.currentPage = None
11+
self.pageCount = None
12+
self._pagination = None
13+
14+
@property
15+
def data(self):
16+
return self.items
17+
18+
@property
19+
def pagination(self):
20+
return {
21+
"totalCount": self.totalCount,
22+
"pageCount": self.pageCount,
23+
"currentPage": self.currentPage,
24+
"perPage": self.perPage
25+
}
26+
27+
@pagination.setter
28+
def pagination(self, value):
29+
if isinstance(value, dict):
30+
self.totalCount = value['totalCount']
31+
self.pageCount = value['pageCount']
32+
self.currentPage = value['currentPage']
33+
self.perPage = value['perPage']
34+
self.limit = self.perPage * self.currentPage
35+
self.offset = self.perPage * (self.currentPage - 1)
36+
37+
@data.setter
38+
def data(self, value):
39+
if isinstance(value, list):
40+
self.count = len(value)
41+
self.items = value
42+

messagebird/client.py

+27-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from messagebird.balance import Balance
77
from messagebird.call import Call
8+
from messagebird.call_list import CallList
89
from messagebird.contact import Contact, ContactList
910
from messagebird.error import Error
1011
from messagebird.group import Group, GroupList
@@ -50,6 +51,10 @@ def __init__(self, errors):
5051
message = ' '.join([str(e) for e in self.errors])
5152
super(ErrorException, self).__init__(message)
5253

54+
class SignleErrorException(Exception):
55+
def __init__(self, errorMessage):
56+
super(SignleErrorException, self).__init__(errorMessage)
57+
5358
class Feature(enum.Enum):
5459
ENABLE_CONVERSATIONS_API_WHATSAPP_SANDBOX = 1
5560

@@ -124,6 +129,18 @@ def call(self,id):
124129
"""Retrieve the information of a specific call"""
125130
return Call().load(self.request('calls/' + str(id), 'GET', None, VOICE_TYPE))
126131

132+
def call_list(self, page=1):
133+
"""Listing calls
134+
135+
Args:
136+
page(int) : The page to list.
137+
Raises:
138+
ErrorException : On api returning errors
139+
140+
Returns:
141+
CallList(object) : The list of calls requested & their status."""
142+
return CallList().load(self.request('calls/?page=' + str(page), 'GET', None, VOICE_TYPE))
143+
127144
def call_create(self, source, destination, callFlow, webhook):
128145
"""Creating a call
129146
@@ -133,16 +150,23 @@ def call_create(self, source, destination, callFlow, webhook):
133150
callFlow(object) : The call flow object to be executed when the call is answered.
134151
webhook(object) : The webhook object containing the url & required token.
135152
Raises:
136-
ErrorException: On api returning errors
153+
ErrorException : On api returning errors
137154
138155
Returns:
139-
If successful, this request will return an object with a data property, which is an array that has a single
140-
call object. If the request failed, an error object will be returned."""
156+
Call(object) : The Call object just created."""
141157

142158
params = locals()
143159
del(params['self'])
144160
return Call().load(self.request('calls', 'POST', params, VOICE_TYPE))
145161

162+
def call_delete(self, id):
163+
"""Delete an existing call object."""
164+
response = self.request_plain_text('calls/' + str(id), 'DELETE', None, VOICE_TYPE)
165+
166+
# successful delete should be empty
167+
if len(response) > 0:
168+
raise SignleErrorException(response)
169+
146170
def hlr(self, id):
147171
"""Retrieve the information of a specific HLR lookup."""
148172
return HLR().load(self.request('hlr/' + str(id)))

tests/test_call.py

+71
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import unittest
33
from messagebird import Client, ErrorException
44
from messagebird.base import Base
5+
from messagebird.client import VOICE_TYPE
56

67
try:
78
from unittest.mock import Mock
@@ -47,6 +48,68 @@ def test_call(self):
4748

4849
self.assertEqual('ended', call.data.status)
4950

51+
def test_call_list(self):
52+
http_client = Mock()
53+
http_client.request.return_value = """
54+
{
55+
"data":[
56+
{
57+
"id":"dda20377-72da-4846-9b2c-0fea3ad4bcb6",
58+
"status":"no_answer",
59+
"source":"16479311111",
60+
"destination":"1416555555",
61+
"createdAt":"2019-08-06T13:17:06Z",
62+
"updatedAt":"2019-08-06T13:17:39Z",
63+
"endedAt":"2019-08-06T13:17:39Z",
64+
"_links":{
65+
"legs":"/calls/dda20377-72da-4846-9b2c-0fea3ad4bcb6/legs",
66+
"self":"/calls/dda20377-72da-4846-9b2c-0fea3ad4bcb6"
67+
}
68+
},
69+
{
70+
"id":"1541535b-9b80-4002-bde5-ed05b5ebed76",
71+
"status":"ended",
72+
"source":"16479311111",
73+
"destination":"1416555556",
74+
"createdAt":"2019-08-06T13:17:06Z",
75+
"updatedAt":"2019-08-06T13:17:39Z",
76+
"endedAt":"2019-08-06T13:17:39Z",
77+
"_links":{
78+
"legs":"/calls/1541535b-9b80-4002-bde5-ed05b5ebed76/legs",
79+
"self":"/calls/1541535b-9b80-4002-bde5-ed05b5ebed76"
80+
}
81+
}
82+
],
83+
"_links": {
84+
"self": "/calls?page=1"
85+
},
86+
"pagination":{
87+
"totalCount":2,
88+
"pageCount":1,
89+
"currentPage":1,
90+
"perPage":10
91+
}
92+
}
93+
"""
94+
95+
callList = Client('', http_client).call_list(page=1)
96+
97+
http_client.request.assert_called_once_with('calls/?page=1', 'GET', None)
98+
99+
# check data is processed
100+
self.assertEqual('no_answer', callList.data[0].status)
101+
self.assertEqual('ended', callList.data[1].status)
102+
103+
# check pagination is passed to object
104+
self.assertEqual(2, callList.totalCount)
105+
self.assertEqual(1, callList.pageCount)
106+
self.assertEqual(1, callList.currentPage)
107+
self.assertEqual(10, callList.perPage)
108+
self.assertEqual(10, callList.pagination['perPage'], 'Check it also supports API pagination format.')
109+
110+
self.assertEqual(0, callList.offset, 'Check it correctly calculates offset.')
111+
self.assertEqual(10, callList.limit, 'Check it correctly calculates limit.')
112+
50113
def test_call_create(self):
51114
api_response = {
52115
"data": [
@@ -102,6 +165,14 @@ def test_call_create(self):
102165
# check it can be formatted as string
103166
self.assertTrue(len(str(call_creation_response)) > 0, 'Check returned call can be formatted as string.')
104167

168+
def test_call_delete(self):
169+
http_client = Mock()
170+
http_client.request.return_value = ''
171+
call_id_to_delete = '21025ed1-cc1d-4554-ac05-043fa6c84e00'
172+
Client('', http_client).call_delete(call_id_to_delete)
173+
174+
http_client.request.assert_called_once_with('calls/%s' % call_id_to_delete, 'DELETE', None)
175+
105176
@staticmethod
106177
def create_expected_call_data_based_on_api_response(api_response):
107178
expected_data = api_response['data'][0]

0 commit comments

Comments
 (0)