Skip to content

Commit 4be2dd3

Browse files
donisdysosmus
authored andcommitted
DEVP-11: Implement call flow methods (#44)
* DEVP-11: Implement get of call flow method * Update messagebird/client.py Co-Authored-By: Stefan R Messagebird <[email protected]> * DEVP-11: Implement listing of call flows * DEVP-11: Implement delete, number list calls * DEVP-11: Implement call flow create and update * DEVP-11: Add an example for call flows * DEVP-11: Use proper names in comments * DEVP-11: Update format calls
1 parent 9d040c6 commit 4be2dd3

File tree

4 files changed

+364
-2
lines changed

4 files changed

+364
-2
lines changed

examples/call_flow.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
import messagebird
5+
6+
#ACCESS_KEY = ''
7+
#CALL_FLOW_ID = ''
8+
9+
try:
10+
ACCESS_KEY
11+
except NameError:
12+
print('You need to set an ACCESS_KEY constant in this file')
13+
sys.exit(1)
14+
15+
try:
16+
CALL_FLOW_ID
17+
except NameError:
18+
print('You need to set a CALL_FLOW_ID constant in this file')
19+
sys.exit(1)
20+
21+
try:
22+
# Create a MessageBird client with the specified ACCESS_KEY.
23+
client = messagebird.Client(ACCESS_KEY)
24+
25+
# Fetch the CallFlow object for the specified CALL_FLOW_ID.
26+
call = client.call_flow(CALL_FLOW_ID)
27+
28+
# Print the object information.
29+
print('\nThe following information was returned as a CallFlow object:\n')
30+
print(' id : {}'.format(call.id))
31+
print(' title : {}'.format(call.title))
32+
print(' steps : ')
33+
for step in call.steps:
34+
print(step)
35+
36+
print(' record : {}'.format(call.record))
37+
print(' default : {}'.format(call.default))
38+
print(' updatedAt : {}'.format(call.updatedAt))
39+
print(' createdAt : {}'.format(call.createdAt))
40+
41+
except messagebird.client.ErrorException as e:
42+
print('\nAn error occured while requesting a CallFlow object:\n')
43+
44+
for error in e.errors:
45+
print(' code : {}'.format(error.code))
46+
print(' description : {}'.format(error.description))
47+
print(' parameter : {}\n'.format(error.parameter))

messagebird/call_flow.py

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
from messagebird.base import Base
2+
from messagebird.base_list import BaseList
3+
4+
5+
class CallFlowList(BaseList):
6+
7+
def __init__(self):
8+
self._data = None
9+
self._pagination = None
10+
11+
super(CallFlowList, self).__init__(CallFlow)
12+
13+
@property
14+
def data(self):
15+
return self._data
16+
17+
@property
18+
def pagination(self):
19+
return self._pagination
20+
21+
@pagination.setter
22+
def pagination(self, value):
23+
self._pagination = value
24+
25+
@data.setter
26+
def data(self, value):
27+
"""Create typed objects from the dicts."""
28+
items = []
29+
for item in value:
30+
items.append(self.itemType().load(item))
31+
32+
self._data = items
33+
34+
35+
class CallFlowNumberList(BaseList):
36+
def __init__(self):
37+
self._data = None
38+
self._pagination = None
39+
40+
super(CallFlowNumberList, self).__init__(CallFlowNumber)
41+
42+
@property
43+
def data(self):
44+
return self._data
45+
46+
@property
47+
def pagination(self):
48+
return self._pagination
49+
50+
@pagination.setter
51+
def pagination(self, value):
52+
self._pagination = value
53+
54+
@data.setter
55+
def data(self, value):
56+
"""Create typed objects from the dicts."""
57+
items = []
58+
for item in value:
59+
items.append(self.itemType().load(item))
60+
61+
self._data = items
62+
63+
class CallFlow(Base):
64+
65+
def __init__(self):
66+
self.id = None
67+
self.title = None
68+
self.record = None
69+
self.steps = None
70+
self.default = None
71+
self._createdAt = None
72+
self._updatedAt = None
73+
74+
@property
75+
def createdAt(self):
76+
return self._createdAt
77+
78+
@createdAt.setter
79+
def createdAt(self, value):
80+
self._createdAt = self.value_to_time(value)
81+
82+
@property
83+
def updatedAt(self):
84+
return self._updatedAt
85+
86+
@updatedAt.setter
87+
def updatedAt(self, value):
88+
self._updatedAt = self.value_to_time(value)
89+
90+
def load(self, data):
91+
if data.get('data') is not None:
92+
items = data.get('data')[0].items()
93+
else:
94+
items = list(data.items())
95+
96+
for name, value in items:
97+
if hasattr(self, name) and not callable(getattr(self, name)):
98+
setattr(self, name, value)
99+
100+
return self
101+
102+
103+
class CallFlowNumber(Base):
104+
def __init__(self):
105+
self.id = None
106+
self.number = None
107+
self.callFlowId = None
108+
self._createdAt = None
109+
self._updatedAt = None
110+
111+
@property
112+
def createdAt(self):
113+
return self._createdAt
114+
115+
@createdAt.setter
116+
def createdAt(self, value):
117+
self._createdAt = self.value_to_time(value)
118+
119+
@property
120+
def updatedAt(self):
121+
return self._updatedAt
122+
123+
@updatedAt.setter
124+
def updatedAt(self, value):
125+
self._updatedAt = self.value_to_time(value)
126+
127+
def load(self, data):
128+
if data.get('data') is not None:
129+
items = data.get('data')[0].items()
130+
else:
131+
items = list(data.items())
132+
133+
for name, value in items:
134+
if hasattr(self, name) and not callable(getattr(self, name)):
135+
setattr(self, name, value)
136+
137+
return self

messagebird/client.py

+28-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from messagebird.conversation import Conversation, ConversationList
2020
from messagebird.conversation_webhook import ConversationWebhook, ConversationWebhookList
2121
from messagebird.voice_recording import VoiceRecordingsList, VoiceRecording
22+
from messagebird.call_flow import CallFlow, CallFlowList, CallFlowNumberList
2223

2324

2425
ENDPOINT = 'https://rest.messagebird.com'
@@ -58,7 +59,7 @@ class Client(object):
5859
def __init__(self, access_key, http_client=None, features=[]):
5960
self.access_key = access_key
6061
self.http_client = http_client
61-
62+
6263
self.conversation_api_root = CONVERSATION_API_WHATSAPP_SANDBOX_ROOT if Feature.ENABLE_CONVERSATIONS_API_WHATSAPP_SANDBOX in features else CONVERSATION_API_ROOT
6364

6465
def _get_http_client(self, type=REST_TYPE):
@@ -67,7 +68,7 @@ def _get_http_client(self, type=REST_TYPE):
6768

6869
if type == CONVERSATION_TYPE:
6970
return HttpClient(self.conversation_api_root, self.access_key, USER_AGENT)
70-
71+
7172
if type == VOICE_TYPE:
7273
return HttpClient(VOICE_API_ROOT, self.access_key, USER_AGENT)
7374

@@ -356,5 +357,30 @@ def voice_recording_download(self, call_id, leg_id, recording_id):
356357
recording_file = self.request_store_as_file(VOICE_API_ROOT + recording_file, recording_id + '.wav')
357358
return VOICE_API_ROOT + recording_file
358359

360+
def call_flow(self, id):
361+
return CallFlow().load(self.request('call-flows/' + str(id), 'GET', None, VOICE_TYPE))
362+
363+
def call_flow_list(self, limit=10, offset=0):
364+
query = self._format_query(limit, offset)
365+
return CallFlowList().load(self.request('call-flows?' + query, 'GET', None, VOICE_TYPE))
366+
367+
def call_flow_create(self, title, steps, default=False, record=False):
368+
params = {'title': title, 'steps': steps, 'default': default, 'record': record}
369+
return CallFlow().load(self.request('call-flows', 'POST', params, VOICE_TYPE))
370+
371+
def call_flow_update(self, id, title, steps, default, record):
372+
params = {'title': title, 'steps': steps, 'default': default, 'record': record}
373+
return CallFlow().load(self.request('call-flows/' + str(id), 'PUT', params, VOICE_TYPE))
374+
375+
def call_flow_delete(self, id):
376+
self.request_plain_text('call-flows/' + str(id), 'DELETE', None, VOICE_TYPE)
377+
378+
def call_flow_numbers_list(self, call_flow_id):
379+
return CallFlowNumberList().load(self.request('call-flows/' + str(call_flow_id) + '/numbers', 'GET', None, VOICE_TYPE))
380+
381+
def call_flow_numbers_add(self, call_flow_id, numbers=()):
382+
params = {'numbers': numbers}
383+
return CallFlowNumberList().load(self.request('call-flows/' + str(call_flow_id) + '/numbers', 'POST', params, VOICE_TYPE))
384+
359385
def _format_query(self, limit, offset):
360386
return 'limit=' + str(limit) + '&offset=' + str(offset)

tests/test_call_flow.py

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import unittest
2+
from messagebird import Client
3+
4+
try:
5+
from unittest.mock import Mock
6+
except ImportError:
7+
# mock was added to unittest in Python 3.3, but was an external library
8+
# before.
9+
from mock import Mock
10+
11+
12+
class TestCallFlow(unittest.TestCase):
13+
14+
def test_get_flow(self):
15+
http_client = Mock()
16+
http_client.request.return_value = '''{
17+
"data": [
18+
{
19+
"id": "de3ed163-d5fc-45f4-b8c4-7eea7458c635",
20+
"title": "Updated call flow",
21+
"record": false,
22+
"steps": [
23+
{
24+
"id": "3538a6b8-5a2e-4537-8745-f72def6bd393",
25+
"action": "transfer",
26+
"options": {
27+
"destination": "31611223344"
28+
}
29+
}
30+
],
31+
"createdAt": "2017-03-06T13:34:14Z",
32+
"updatedAt": "2017-03-06T15:02:38Z"
33+
}
34+
],
35+
"_links": {
36+
"self": "/call-flows/de3ed163-d5fc-45f4-b8c4-7eea7458c635"
37+
}
38+
}
39+
'''
40+
41+
call_flow = Client('', http_client).call_flow('de3ed163-d5fc-45f4-b8c4-7eea7458c635')
42+
http_client.request.assert_called_once_with('call-flows/de3ed163-d5fc-45f4-b8c4-7eea7458c635', 'GET', None)
43+
44+
self.assertEqual('Updated call flow', call_flow.title)
45+
self.assertIsNotNone(call_flow.steps)
46+
47+
def test_get_flow_list(self):
48+
http_client = Mock()
49+
http_client.request.return_value = '''{
50+
"data": [
51+
{
52+
"id": "de3ed163-d5fc-45f4-b8c4-7eea7458c635",
53+
"title": "Forward call to 31612345678",
54+
"record": false,
55+
"steps": [
56+
{
57+
"id": "3538a6b8-5a2e-4537-8745-f72def6bd393",
58+
"action": "transfer",
59+
"options": {
60+
"destination": "31612345678"
61+
}
62+
}
63+
],
64+
"createdAt": "2017-03-06T13:34:14Z",
65+
"updatedAt": "2017-03-06T13:34:14Z",
66+
"_links": {
67+
"self": "/call-flows/de3ed163-d5fc-45f4-b8c4-7eea7458c635"
68+
}
69+
},
70+
{
71+
"id": "de3ed163-d5fc-45f4-b8c4-7eea7458c634",
72+
"title": "Forward call to 0600123123",
73+
"record": true,
74+
"steps": [
75+
{
76+
"id": "3538a6b8-5a2e-4537-8745-f72def6bd393",
77+
"action": "transfer",
78+
"options": {
79+
"destination": "31612345678"
80+
}
81+
}
82+
],
83+
"createdAt": "2017-03-06T13:34:14Z",
84+
"updatedAt": "2017-03-06T13:34:14Z",
85+
"_links": {
86+
"self": "/call-flows/de3ed163-d5fc-45f4-b8c4-7eea7458c634"
87+
}
88+
}
89+
],
90+
"_links": {
91+
"self": "/call-flows?page=1"
92+
},
93+
"pagination": {
94+
"totalCount": 2,
95+
"pageCount": 2,
96+
"currentPage": 1,
97+
"perPage": 10
98+
}
99+
}
100+
'''
101+
102+
call_flow_list = Client('', http_client).call_flow_list(20, 0)
103+
104+
http_client.request.assert_called_once_with('call-flows?limit=20&offset=0', 'GET', None)
105+
106+
self.assertEqual('Forward call to 0600123123', call_flow_list.data[1].title)
107+
self.assertEqual(2, call_flow_list.pagination['totalCount'])
108+
109+
def test_numbers_list(self):
110+
http_client = Mock()
111+
http_client.request.return_value = '''{
112+
"data": [
113+
{
114+
"id": "13f38f34-7ff4-45b3-8783-8d5b1143f22b",
115+
"number": "31611111111",
116+
"callFlowId": "de3ed163-d5fc-45f4-b8c4-7eea7458c635",
117+
"createdAt": "2017-03-16T13:49:24Z",
118+
"updatedAt": "2017-09-12T08:59:50Z",
119+
"_links": {
120+
"self": "/numbers/13f38f34-7ff4-45b3-8783-8d5b1143f22b"
121+
}
122+
}
123+
],
124+
"_links": {
125+
"self": "/call-flows/de3ed163-d5fc-45f4-b8c4-7eea7458c635/numbers?page=1"
126+
},
127+
"pagination": {
128+
"totalCount": 1,
129+
"pageCount": 1,
130+
"currentPage": 1,
131+
"perPage": 10
132+
}
133+
}
134+
'''
135+
136+
number_list = Client('', http_client).call_flow_numbers_list('de3ed163-d5fc-45f4-b8c4-7eea7458c635')
137+
138+
http_client.request.assert_called_once_with('call-flows/de3ed163-d5fc-45f4-b8c4-7eea7458c635/numbers', 'GET', None)
139+
140+
self.assertEqual('31611111111', number_list.data[0].number)
141+
self.assertEqual(1, number_list.pagination['totalCount'])
142+
143+
def test_numbers_add(self):
144+
http_client = Mock()
145+
http_client.request.return_value = '{}'
146+
147+
Client('', http_client).call_flow_numbers_add('de3ed163-d5fc-45f4-b8c4-7eea7458c635',
148+
['31611111111', '31611111112'])
149+
150+
params = {'numbers': ['31611111111', '31611111112']}
151+
152+
http_client.request.assert_called_once_with('call-flows/de3ed163-d5fc-45f4-b8c4-7eea7458c635/numbers', 'POST', params)

0 commit comments

Comments
 (0)