Skip to content

Commit d2361ef

Browse files
authored
DX-2903 add <Record> BXML (#121)
* DX-2903 Add `<Record>` BXML * Trigger Tests * Remove `inspect` * Refactor existing verbs for changes to `attributes` * Document attributes type * Make `attributes` property private
1 parent 327a11f commit d2361ef

File tree

10 files changed

+165
-34
lines changed

10 files changed

+165
-34
lines changed

bandwidth/model/bxml/terminal_verb.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@ class TerminalVerb(Verb):
1212
"""Base class for BXML verbs
1313
"""
1414

15-
def __init__(self, tag: str, content: str = None, attributes: dict = None):
15+
def __init__(self, tag: str, content: str = None):
1616
"""Initialize the verb model
1717
1818
Args:
1919
tag (str): Name of the XML element
2020
content (str, optional): XML element content. Defaults to None.
21-
attributes (dict, optional): XML element attributes. Defaults to None.
2221
"""
23-
super().__init__(tag=tag, content=content, attributes=attributes, nested_verbs=None)
22+
super().__init__(tag=tag, content=content, nested_verbs=None)
2423

2524
def add_verb(self, verb: Verb):
2625
"""Adding verbs is not allowed for this class

bandwidth/model/bxml/verb.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,32 @@
66
@copyright Bandwidth INC
77
"""
88
from __future__ import annotations
9+
from typing import Union
910
import xml.etree.ElementTree as ET
1011

1112

1213
class Verb:
1314
"""Base class for BXML verbs
1415
"""
1516

16-
def __init__(self, tag: str, content: str = None, attributes: dict = None, nested_verbs: list[Verb] = None):
17+
def __init__(self, tag: str, content: str = None, nested_verbs: list[Verb] = None):
1718
"""Initialize the verb model
1819
1920
Args:
2021
tag (str): Name of the XML element
2122
content (str, optional): XML element content. Defaults to None.
22-
attributes (dict, optional): XML element attributes. Defaults to None.
2323
nested_verbs (list[BxmlVerb], optional): XML element children. Defaults to None.
2424
"""
2525
self._tag = tag
2626
self._content = content
27-
self._attributes = attributes
2827
self._nested_verbs = nested_verbs
2928
if not self._nested_verbs:
3029
self._nested_verbs = []
3130

31+
@property
32+
def _attributes(self) -> Union[None, dict]:
33+
return None
34+
3235
def __len__(self) -> int:
3336
"""Override default len method. Returns length of _nested_verbs array
3437
@@ -54,7 +57,7 @@ def _set_attributes(self, root: ET.Element):
5457
Args:
5558
root (ET.Element): XML Element to add attributes to
5659
"""
57-
if self._attributes:
60+
if self._attributes is not None:
5861
for key, value in self._attributes.items():
5962
if value is not None:
6063
root.set(key, value)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .bridge import Bridge
22
from .pause_recording import PauseRecording
33
from .phone_number import PhoneNumber
4+
from .record import Record
45
from .sip_uri import SipUri
56
from .tag import Tag
67
from .transfer import Transfer

bandwidth/model/bxml/verbs/bridge.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,14 @@ def __init__(
5555
self.fallback_username = fallback_username
5656
self.fallback_password = fallback_password
5757
self.tag = tag
58-
self.attributes = {
58+
super().__init__(
59+
tag="Bridge",
60+
content=self.target_call,
61+
)
62+
63+
@property
64+
def _attributes(self):
65+
return {
5966
"bridgeCompleteUrl": self.bridge_complete_url,
6067
"bridgeCompleteMethod": self.bridge_complete_method,
6168
"bridgeCompleteFallbackUrl": self.bridge_complete_fallback_url,
@@ -70,8 +77,3 @@ def __init__(
7077
"fallbackPassword": self.fallback_password,
7178
"tag": self.tag
7279
}
73-
super().__init__(
74-
tag="Bridge",
75-
content=self.target_call,
76-
attributes=self.attributes
77-
)

bandwidth/model/bxml/verbs/phone_number.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ def __init__(
4444
self.fallback_username = fallback_username
4545
self.fallback_password = fallback_password
4646
self.tag = tag
47-
self.attributes = {
47+
super().__init__(
48+
tag="PhoneNumber",
49+
content=self.number
50+
)
51+
52+
@property
53+
def _attributes(self):
54+
return {
4855
"transferAnswerUrl": self.transfer_answer_url,
4956
"transferAnswerMethod": self.transfer_answer_method,
5057
"transferAnswerFallbackUrl": self.transfer_answer_fallback_url,
@@ -57,8 +64,3 @@ def __init__(
5764
"fallbackPassword": self.fallback_password,
5865
"tag": self.tag
5966
}
60-
super().__init__(
61-
tag="PhoneNumber",
62-
content=self.number,
63-
attributes=self.attributes
64-
)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
record.py
3+
4+
Bandwidth's Record BXML verb
5+
6+
@copyright Bandwidth INC
7+
"""
8+
from ..terminal_verb import TerminalVerb
9+
10+
11+
class Record(TerminalVerb):
12+
13+
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,
22+
password: str=None, fallback_username: str=None,
23+
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
26+
):
27+
"""Initialize a <Record> verb
28+
29+
Args:
30+
record_complete_url (str, optional): URL to send the Record Complete event to once the recording has ended. Accepts BXML, and may be a relative URL. This callback will not be sent if the recording ended due to the call hanging up. Defaults to None.
31+
record_complete_method (str, optional): The HTTP method to use for the request to recordCompleteUrl. GET or POST. Default value is POST. Defaults to None.
32+
record_complete_fallback_url (str, optional): A fallback url which, if provided, will be used to retry the Record Complete callback delivery in case recordCompleteUrl fails to respond. Defaults to None.
33+
record_complete_fallback_method (str, optional): The HTTP method to use to deliver the Record Complete callback to recordCompleteFallbackUrl. GET or POST. Default value is POST. Defaults to None.
34+
recording_available_url (str, optional): URL to send the Recording Available event to once it has been processed. Does not accept BXML. May be a relative URL. Defaults to None.
35+
recording_available_method (str, optional): The HTTP method to use for the request to recordingAvailableUrl. GET or POST. Default value is POST. Defaults to None.
36+
transcribe (str, optional): A boolean value to indicate that recording should be transcribed. Transcription can succeed only for recordings of length greater than 500 milliseconds and less than 4 hours. Default is false. Defaults to None.
37+
transcription_available_url (str, optional): URL to send the Transcription Available event to once it has been processed. Does not accept BXML. May be a relative URL. Defaults to None.
38+
transcription_available_method (str, optional): The HTTP method to use for the request to transcriptionAvailableUrl. GET or POST. Default value is POST. Defaults to None.
39+
username (str, optional): The username to send in the HTTP request to recordCompleteUrl, recordingAvailableUrl or transcriptionAvailableUrl. If specified, the URLs must be TLS-encrypted (i.e., https). Defaults to None.
40+
password (str, optional): The password to send in the HTTP request to recordCompleteUrl, recordingAvailableUrl or transcriptionAvailableUrl. If specified, the URLs must be TLS-encrypted (i.e., https). Defaults to None.
41+
fallback_username (str, optional): The username to send in the HTTP request to recordCompleteFallbackUrl. If specified, the URLs must be TLS-encrypted (i.e., https). Defaults to None.
42+
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.
43+
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.
44+
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.
46+
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.
47+
file_format (str, optional): The audio format that the recording will be saved as: mp3 or wav. Default value is wav. Defaults to None.
48+
"""
49+
self.record_complete_url = record_complete_url
50+
self.record_complete_method = record_complete_method
51+
self.record_complete_fallback_url = record_complete_fallback_url
52+
self.record_complete_fallback_method = record_complete_fallback_method
53+
self.recording_available_url = recording_available_url
54+
self.recording_available_method = recording_available_method
55+
self.transcribe = transcribe
56+
self.transcription_available_url = transcription_available_url
57+
self.transcription_available_method = transcription_available_method
58+
self.username = username
59+
self.password = password
60+
self.fallback_username = fallback_username
61+
self.fallback_password = fallback_password
62+
self.tag = tag
63+
self.terminating_digits = terminating_digits
64+
self.max_duration = max_duration
65+
self.silence_timeout = silence_timeout
66+
self.file_format = file_format
67+
super().__init__(tag="Record", content=None)
68+
69+
@property
70+
def _attributes(self):
71+
return {
72+
"recordCompleteUrl": self.record_complete_url,
73+
"recordCompleteMethod": self.record_complete_method,
74+
"recordCompleteFallback_url": self.record_complete_fallback_url,
75+
"recordCompleteFallback_method": self.record_complete_fallback_method,
76+
"recordingAvailableUrl": self.recording_available_url,
77+
"recordingAvailableMethod": self.recording_available_method,
78+
"transcribe": self.transcribe,
79+
"transcriptionAvailableUrl": self.transcription_available_url,
80+
"transcriptionAvailableMethod": self.transcription_available_method,
81+
"username": self.username,
82+
"password": self.password,
83+
"fallbackUsername": self.fallback_username,
84+
"fallbackPassword": self.fallback_password,
85+
"tag": self.tag,
86+
"terminatingDigits": self.terminating_digits,
87+
"maxDuration": self.max_duration,
88+
"silenceTimeout": self.silence_timeout,
89+
"fileFormat": self.file_format
90+
}

bandwidth/model/bxml/verbs/sip_uri.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,15 @@ def __init__(
4646
self.fallback_username = fallback_username
4747
self.fallback_password = fallback_password
4848
self.tag = tag
49-
self.attributes = {
50-
"uui": uui,
49+
super().__init__(
50+
tag="SipUri",
51+
content=self.uri,
52+
)
53+
54+
@property
55+
def _attributes(self):
56+
return {
57+
"uui": self.uui,
5158
"transferAnswerUrl": self.transfer_answer_url,
5259
"transferAnswerMethod": self.transfer_answer_method,
5360
"transferAnswerFallbackUrl": self.transfer_answer_fallback_url,
@@ -60,8 +67,3 @@ def __init__(
6067
"fallbackPassword": self.fallback_password,
6168
"tag": self.tag
6269
}
63-
super().__init__(
64-
tag="SipUri",
65-
content=self.uri,
66-
attributes=self.attributes
67-
)

bandwidth/model/bxml/verbs/tag.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ def __init__(self, content=""):
1717
content (str, optional): Custom tag value. Defaults to "".
1818
"""
1919
self.content = content
20-
super().__init__(tag="Tag", content=self.content, attributes=None)
20+
super().__init__(tag="Tag", content=self.content)

bandwidth/model/bxml/verbs/transfer.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,15 @@ def __init__(
7575
self.tag = tag
7676
self.diversion_treatment = diversion_treatment
7777
self.diversion_reason = diversion_reason
78-
self.attributes = {
78+
super().__init__(
79+
tag="Transfer",
80+
content=None,
81+
nested_verbs=self.transfer_to
82+
)
83+
84+
@property
85+
def _attributes(self):
86+
return {
7987
"transferCallerId": self.transfer_caller_id,
8088
"callTimeout": self.call_timeout,
8189
"transferCompleteUrl": self.transfer_complete_url,
@@ -90,12 +98,6 @@ def __init__(
9098
"diversionTreatment": self.diversion_treatment,
9199
"diversionReason": self.diversion_reason
92100
}
93-
super().__init__(
94-
tag="Transfer",
95-
content=None,
96-
attributes=self.attributes,
97-
nested_verbs=self.transfer_to
98-
)
99101

100102
def add_transfer_recipient(self, recipient: Union[PhoneNumber, SipUri]):
101103
super().add_verb(recipient)

test/unit/bxml/test_record.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
test_record.py
3+
4+
Unit tests for the <Record> BXML verb
5+
6+
@copyright Bandwidth Inc.
7+
"""
8+
import pytest
9+
import unittest
10+
11+
from bandwidth.model.bxml.verb import Verb
12+
from bandwidth.model.bxml.verbs.record import Record
13+
14+
15+
class TestRecord(unittest.TestCase):
16+
17+
def setUp(self):
18+
self.record = Record()
19+
self.record.max_duration = "10"
20+
self.test_verb = Verb(tag="test")
21+
22+
def test_to_bxml(self):
23+
expected = '<Record maxDuration="10" />'
24+
assert(expected == self.record.to_bxml())
25+
26+
27+
def test_add_verb(self):
28+
with pytest.raises(AttributeError):
29+
self.record.add_verb(self.test_verb)
30+

0 commit comments

Comments
 (0)