Skip to content

Commit 409348f

Browse files
authored
DX-2855 Update BXML library (#133)
* Set pause duration to be Int * Fix `Record.max_duration` * Update `ring.duration` and `ring.answer_call` test * Update int fields for `SendDtmf` * Fix Conference Not allowed to have nested verbs * Set `Gather` int properties to int * Set proper bool values for `StartRecording` * DRY * Fix `Ring` test
1 parent bee66a1 commit 409348f

33 files changed

+139
-279
lines changed

bandwidth/model/bxml/verbs/conference.py

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,18 @@
55
66
@copyright Bandwidth INC
77
"""
8-
from typing import Union, List
9-
from ..verb import Verb
10-
from .play_audio import PlayAudio
11-
from .speak_sentence import SpeakSentence
12-
from .start_recording import StartRecording
13-
from .stop_recording import StopRecording
14-
from .pause_recording import PauseRecording
15-
from .resume_recording import ResumeRecording
8+
from ..terminal_verb import TerminalVerb
169

1710

18-
class Conference(Verb):
11+
class Conference(TerminalVerb):
1912

2013
def __init__(
21-
self, name: str,
22-
audio_and_recording_verbs: List[Union[PlayAudio, SpeakSentence, StartRecording, StopRecording, PauseRecording, ResumeRecording]] = [], mute: str=None,
23-
hold: str=None, call_ids_to_coach: str=None,
24-
conference_event_url: str=None, conference_event_method: str=None,
25-
conference_event_fallback_url: str=None, conference_event_fallback_method: str=None,
14+
self, name: str, mute: str=None,
15+
hold: str=None, call_ids_to_coach: str=None,
16+
conference_event_url: str=None, conference_event_method: str=None,
17+
conference_event_fallback_url: str=None, conference_event_fallback_method: str=None,
2618
username: str=None, password: str=None,
27-
fallback_username: str=None, fallback_password: str=None,
19+
fallback_username: str=None, fallback_password: str=None,
2820
tag: str=None, callback_timeout: str=None,
2921
):
3022
"""Initialize a <Conference> verb
@@ -33,10 +25,10 @@ def __init__(
3325
name (str): The name of the conference. Can contain up to 100 characters of letters, numbers, and the symbols -, _, and .
3426
mute (str, optional): A boolean value to indicate whether the member should be on mute in the conference. When muted, a member can hear others speak, but others cannot hear them speak. Defaults to false.
3527
hold (str, optional): A boolean value to indicate whether the member should be on hold in the conference. When on hold, a member cannot hear others, and they cannot be heard. Defaults to false.
36-
call_ids_to_coach (str, optional): A comma-separated list of call ids to coach. When a call joins a conference with this attribute set, it will coach the listed calls.
28+
call_ids_to_coach (str, optional): A comma-separated list of call ids to coach. When a call joins a conference with this attribute set, it will coach the listed calls.
3729
Those calls will be able to hear and be heard by the coach, but other calls in the conference will not hear the coach.
38-
conference_event_url (str, optional): URL to send Conference events to. The URL, method, username, and password are set by the BXML document that creates the conference,
39-
and all events related to that conference will be delivered to that same endpoint. If more calls join afterwards and also have this property (or any other webhook related properties like username and password),
30+
conference_event_url (str, optional): URL to send Conference events to. The URL, method, username, and password are set by the BXML document that creates the conference,
31+
and all events related to that conference will be delivered to that same endpoint. If more calls join afterwards and also have this property (or any other webhook related properties like username and password),
4032
they will be ignored and the original webhook information will be used. This URL may be a relative endpoint.
4133
conference_event_method (str, optional): The HTTP method to use for the request to conferenceEventUrl. GET or POST. Default value is POST.
4234
conference_event_fallback_url (str, optional): A fallback url which, if provided, will be used to retry the conference webhook deliveries in case conferenceEventUrl fails to respond.
@@ -45,17 +37,17 @@ def __init__(
4537
password (str, optional): The password to send in the HTTP request to conferenceEventUrl.
4638
fallback_username (str, optional): The username to send in the HTTP request to conferenceEventFallbackUrl.
4739
fallback_password (str, optional): The password to send in the HTTP request to conferenceEventFallbackUrl.
48-
tag (str, optional): A custom string that will be sent with this and all future callbacks unless overwritten by a future tag attribute or <Tag> verb, or cleared. May be cleared by setting tag="".
40+
tag (str, optional): A custom string that will be sent with this and all future callbacks unless overwritten by a future tag attribute or <Tag> verb, or cleared. May be cleared by setting tag="".
4941
Max length 256 characters. Defaults to None.
50-
callback_timeout (str, optional): This is the timeout (in seconds) to use when delivering webhooks for the conference.
42+
callback_timeout (str, optional): This is the timeout (in seconds) to use when delivering webhooks for the conference.
5143
If not set, it will inherit the webhook timeout from the call that creates the conference. Can be any numeric value (including decimals) between 1 and 25.
5244
5345
Nested Verbs:
54-
PlayAudio: (optional)
55-
SpeakSentence: (optional)
56-
StartRecording: (optional)
46+
PlayAudio: (optional)
47+
SpeakSentence: (optional)
48+
StartRecording: (optional)
5749
StopRecording: (optional)
58-
PauseRecording: (optional)
50+
PauseRecording: (optional)
5951
ResumeRecording: (optional)
6052
"""
6153
self.name = name
@@ -72,11 +64,10 @@ def __init__(
7264
self.fallback_password = fallback_password
7365
self.tag = tag
7466
self.callback_timeout = callback_timeout
75-
self.audio_and_recording_verbs = audio_and_recording_verbs
7667
super().__init__(
77-
tag="Conference",
78-
content=self.name,
79-
nested_verbs=self.audio_and_recording_verbs)
68+
tag="Conference",
69+
content=self.name
70+
)
8071

8172
@property
8273
def _attributes(self):

bandwidth/model/bxml/verbs/gather.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ class Gather(Verb):
1515

1616
def __init__(
1717
self, audio_verbs: List[Union[PlayAudio, SpeakSentence]] = [],
18-
gather_url: str=None, gather_method: str=None,
19-
gather_fallback_url: str=None, gather_fallback_method: str=None,
18+
gather_url: str=None, gather_method: str=None,
19+
gather_fallback_url: str=None, gather_fallback_method: str=None,
2020
username: str=None, password: str=None,
21-
fallback_username: str=None, fallback_password: str=None,
21+
fallback_username: str=None, fallback_password: str=None,
2222
tag: str=None, terminating_digits: str=None,
23-
max_digits: str=None, inter_digit_timeout: str=None,
24-
first_digit_timeout: str=None, repeat_count: str=None
23+
max_digits: int=None, inter_digit_timeout: int=None,
24+
first_digit_timeout: int=None, repeat_count: int=None
2525
):
2626
"""Initialize a <Gather> verb
2727
@@ -34,14 +34,14 @@ def __init__(
3434
password (str, optional): The password to send in the HTTP request to gather_url.
3535
fallback_username (str, optional): The username to send in the HTTP request to gather_fallback_url.
3636
fallback_password (str, optional): The password to send in the HTTP request to gather_fallback_url.
37-
tag (str, optional): A custom string that will be sent with this and all future callbacks unless overwritten by a future tag attribute or <Tag> verb, or cleared.
37+
tag (str, optional): A custom string that will be sent with this and all future callbacks unless overwritten by a future tag attribute or <Tag> verb, or cleared.
3838
May be cleared by setting tag="". Max length 256 characters.
3939
terminating_digits (str, optional): When any of these digits are pressed, it will terminate the Gather. Default value is "", which disables this feature.
40-
max_digits (str, optional): Max number of digits to collect. Default value is 50. Range: decimal values between 1 - 50.
41-
inter_digit_timeout (str, optional): Time (in seconds) allowed between digit presses before automatically terminating the Gather. Default value is 5. Range: decimal values between 1 - 60.
42-
first_digit_timeout (str, optional): Time (in seconds) to pause after any audio from nested <SpeakSentence> or <PlayAudio> verb is played (in seconds) before terminating the Gather.
40+
max_digits (int, optional): Max number of digits to collect. Default value is 50. Range: decimal values between 1 - 50.
41+
inter_digit_timeout (int, optional): Time (in seconds) allowed between digit presses before automatically terminating the Gather. Default value is 5. Range: decimal values between 1 - 60.
42+
first_digit_timeout (int, optional): Time (in seconds) to pause after any audio from nested <SpeakSentence> or <PlayAudio> verb is played (in seconds) before terminating the Gather.
4343
Default value is 5. Range: decimal values between 0 - 60.
44-
repeat_count (str, optional): The number of times the audio prompt should be played if no digits are pressed. For example, if this value is 3, the nested audio clip will be played a maximum of three times.
44+
repeat_count (int, optional): The number of times the audio prompt should be played if no digits are pressed. For example, if this value is 3, the nested audio clip will be played a maximum of three times.
4545
The delay between repetitions will be equal to first_digit_timeout. Default value is 1. repeat_count * number of verbs must not be greater than 20.
4646
4747
Nested Verbs:
@@ -64,7 +64,7 @@ def __init__(
6464
self.repeat_count = repeat_count
6565
self.audio_verbs = audio_verbs
6666
super().__init__(
67-
tag="Gather",
67+
tag="Gather",
6868
nested_verbs=self.audio_verbs)
6969

7070
@property
@@ -80,8 +80,8 @@ def _attributes(self):
8080
"fallbackPassword": self.fallback_password,
8181
"tag": self.tag,
8282
"terminatingDigits": self.terminating_digits,
83-
"maxDigits": self.max_digits,
84-
"interDigitTimeout": self.inter_digit_timeout,
85-
"firstDigitTimeout": self.first_digit_timeout,
86-
"repeatCount": self.repeat_count,
83+
"maxDigits": str(self.max_digits),
84+
"interDigitTimeout": str(self.inter_digit_timeout),
85+
"firstDigitTimeout": str(self.first_digit_timeout),
86+
"repeatCount": str(self.repeat_count),
8787
}

bandwidth/model/bxml/verbs/pause.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@
66
@copyright Bandwidth INC
77
"""
88
from ..terminal_verb import TerminalVerb
9+
10+
911
class Pause(TerminalVerb):
10-
def __init__(self, duration:str=None):
12+
def __init__(self, duration:int=1):
1113
"""Initialize a <Pause> verb
1214
Args:
1315
duration (str, optional): The time in seconds to pause. Default value is 1.
1416
"""
15-
self.duration = duration
17+
self.duration = str(duration)
1618

1719
super().__init__(tag="Pause")
18-
20+
1921
@property
2022
def _attributes(self):
2123
return {
2224
"duration": self.duration
23-
}
25+
}

bandwidth/model/bxml/verbs/record.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@
1111
class Record(TerminalVerb):
1212

1313
def __init__(
14-
self, record_complete_url: str = None,
15-
record_complete_method: str = None,
16-
record_complete_fallback_url: str = None,
17-
record_complete_fallback_method: str = None,
18-
recording_available_url: str = None,
19-
recording_available_method: str = None,
20-
transcribe: str = None, transcription_available_url: str = None,
21-
transcription_available_method: str = None, username: str=None,
14+
self, record_complete_url: str=None,
15+
record_complete_method: str=None,
16+
record_complete_fallback_url: str=None,
17+
record_complete_fallback_method: str=None,
18+
recording_available_url: str=None,
19+
recording_available_method: str=None,
20+
transcribe: str=None, transcription_available_url: str=None,
21+
transcription_available_method: str=None, username: str=None,
2222
password: str=None, fallback_username: str=None,
2323
fallback_password: str=None, tag: str=None,
24-
terminating_digits: str = None, max_duration: str = None,
25-
silence_timeout: str = None, file_format: str = None
24+
terminating_digits: str=None, max_duration: int=None,
25+
silence_timeout: str=None, file_format: str=None
2626
):
2727
"""Initialize a <Record> verb
2828
@@ -42,7 +42,7 @@ def __init__(
4242
fallback_password (str, optional): The password to send in the HTTP request to recordCompleteFallbackUrl. If specified, the URLs must be TLS-encrypted (i.e., https). Defaults to None.
4343
tag (str, optional): A custom string that will be sent with this and all future callbacks unless overwritten by a future tag attribute or <Tag> verb, or cleared. May be cleared by setting tag="". Max length 256 characters. Defaults to None.
4444
terminating_digits (str, optional): When pressed, this digit will terminate the recording. Default value is “#”. This feature can be disabled with "". Defaults to None.
45-
max_duration (str, optional): Maximum length of recording (in seconds). Max 10800 (3 hours). Default value is 60. Defaults to None.
45+
max_duration (int, optional): Maximum length of recording (in seconds). Max 10800 (3 hours). Default value is 60. Defaults to None.
4646
silence_timeout (str, optional): Length of silence after which to end the recording (in seconds). Max is equivalent to the maximum maxDuration value. Default value is 0, which means no timeout. Defaults to None.
4747
file_format (str, optional): The audio format that the recording will be saved as: mp3 or wav. Default value is wav. Defaults to None.
4848
"""
@@ -64,6 +64,7 @@ def __init__(
6464
self.max_duration = max_duration
6565
self.silence_timeout = silence_timeout
6666
self.file_format = file_format
67+
6768
super().__init__(tag="Record", content=None)
6869

6970
@property
@@ -84,7 +85,7 @@ def _attributes(self):
8485
"fallbackPassword": self.fallback_password,
8586
"tag": self.tag,
8687
"terminatingDigits": self.terminating_digits,
87-
"maxDuration": self.max_duration,
88+
"maxDuration": str(self.max_duration),
8889
"silenceTimeout": self.silence_timeout,
8990
"fileFormat": self.file_format
9091
}

bandwidth/model/bxml/verbs/ring.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
class Ring(TerminalVerb):
1212

1313
def __init__(
14-
self, duration: str = None,
15-
answer_call: str = None,
14+
self, duration: int=None,
15+
answer_call: bool=None,
1616
):
1717
"""Initialize a <Ring> verb
1818
1919
Args:
20-
duration (str, optional): How many seconds to play ringing on the call. Default value is 5. Range: decimal values between 0.1 - 86400.
21-
answer_call (str, optional): A boolean indicating whether or not to answer the call when Ring is executed on an unanswered incoming call. Default value is 'true'.
20+
duration (int, optional): How many seconds to play ringing on the call. Default value is 5. Range: decimal values between 0.1 - 86400.
21+
answer_call (bool, optional): A boolean indicating whether or not to answer the call when Ring is executed on an unanswered incoming call. Default value is 'true'.
2222
"""
2323
self.duration = duration
2424
self.answer_call = answer_call
@@ -27,6 +27,6 @@ def __init__(
2727
@property
2828
def _attributes(self):
2929
return {
30-
"duration": self.duration,
31-
"answerCall": self.answer_call,
30+
"duration": str(self.duration),
31+
"answerCall": str(self.answer_call),
3232
}

bandwidth/model/bxml/verbs/send_dtmf.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ class SendDtmf(TerminalVerb):
1212

1313
def __init__(
1414
self, digits: str,
15-
tone_duration: str=None,
16-
tone_interval: str=None,
15+
tone_duration: int=None,
16+
tone_interval: int=None,
1717
):
1818
"""Initialize a <SendDtmf> verb
1919
2020
Args:
2121
digits (str): String containing the DTMF characters to be sent in a call. Allows a maximum of 50 characters. The digits will be sent one-by-one with a marginal delay.
22-
tone_duration (str, optional): The length (in milliseconds) of each DTMF tone. Default value is 200. Range: decimal values between 50 - 5000.
23-
tone_interval (str, optional): The duration of silence (in milliseconds) following each DTMF tone. Default value is 400. Range: decimal values between 50 - 5000.
22+
tone_duration (int, optional): The length (in milliseconds) of each DTMF tone. Default value is 200. Range: decimal values between 50 - 5000.
23+
tone_interval (int, optional): The duration of silence (in milliseconds) following each DTMF tone. Default value is 400. Range: decimal values between 50 - 5000.
2424
"""
2525
self.digits = digits
2626
self.tone_duration = tone_duration
@@ -33,6 +33,6 @@ def __init__(
3333
@property
3434
def _attributes(self):
3535
return {
36-
"toneDuration": self.tone_duration,
37-
"toneInterval": self.tone_interval
36+
"toneDuration": str(self.tone_duration),
37+
"toneInterval": str(self.tone_interval)
3838
}

bandwidth/model/bxml/verbs/start_recording.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ class StartRecording(TerminalVerb):
1313
def __init__(
1414
self, recording_available_url: str = None,
1515
recording_available_method: str = None,
16-
transcribe: str = None, transcription_available_url: str = None,
16+
transcribe: bool = None, transcription_available_url: str = None,
1717
transcription_available_method: str = None, username: str=None,
1818
password: str=None, tag: str=None,
19-
file_format: str = None, multi_channel: str = None
19+
file_format: str = None, multi_channel: bool = None
2020
):
2121
"""Initialize a <StartRecording> verb
2222
@@ -50,12 +50,12 @@ def _attributes(self):
5050
return {
5151
"recordingAvailableUrl": self.recording_available_url,
5252
"recordingAvailableMethod": self.recording_available_method,
53-
"transcribe": self.transcribe,
53+
"transcribe": str(self.transcribe),
5454
"transcriptionAvailableUrl": self.transcription_available_url,
5555
"transcriptionAvailableMethod": self.transcription_available_method,
5656
"username": self.username,
5757
"password": self.password,
5858
"tag": self.tag,
5959
"fileFormat": self.file_format,
60-
"multiChannel": self.multi_channel
60+
"multiChannel": str(self.multi_channel)
6161
}

test/unit/bxml/test_base_classes.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,45 @@
66
77
@copyright Bandwidth Inc.
88
"""
9+
import pytest
910
import unittest
1011

1112
from bandwidth.model.bxml.root import Root
1213
from bandwidth.model.bxml.verb import Verb
14+
from bandwidth.model.bxml.terminal_verb import TerminalVerb
1315

1416

1517
class TestBaseClasses(unittest.TestCase):
16-
17-
def setUp(self):
18+
19+
def setUp(self):
1820
self.root = Root(tag="TestRoot")
1921
self.verb1 = Verb(tag="TestVerb1", content="test")
2022
self.verb2 = Verb(tag="TestVerb2")
2123
self.verb3 = Verb(tag="TestVerb3")
22-
24+
self.terminal_verb = TerminalVerb(tag="TestTerminalVerb")
25+
2326
def test_root(self):
2427
self.root.add_verb(self.verb1)
2528
self.root.add_verb(self.verb2)
26-
29+
2730
expected_bxml = "<?xml version='1.0' encoding='utf8'?>\n<TestRoot><TestVerb1>test</TestVerb1><TestVerb2 /></TestRoot>"
2831
assert(type(self.root[0]) == Verb)
2932
assert(len(self.root) == 2)
3033
assert(expected_bxml == self.root.to_bxml())
31-
34+
3235
def test_verb(self):
3336
self.verb3.add_verb(self.verb1)
3437

3538
expected_bxml = "<TestVerb3><TestVerb1>test</TestVerb1></TestVerb3>"
3639
assert(type(self.verb3[0]) == Verb)
3740
assert(len(self.verb3) == 1)
3841
assert(expected_bxml == self.verb3.to_bxml())
39-
42+
4043
def test_adding_verbs_to_root_during_creation(self):
4144
self.root2 = Root(tag="TestRoot2", nested_verbs=[self.verb1, self.verb2])
4245

4346
assert(len(self.root2) == 2)
4447

48+
def test_adding_verbs_to_terminal_verb(self):
49+
with pytest.raises(AttributeError):
50+
self.terminal_verb.add_verb(self.verb1)

0 commit comments

Comments
 (0)