Skip to content

Commit 5ad427c

Browse files
authored
DX-2870 Refactor of BXML Module (#113)
* DX-2870 Create Base BXML Classes Created base class for root (`<Bxml>` and `<Response>`) Created base class for verbs Wrote `<Tag>` verb to do some basic testing * Add docstrings * Refactor `BxmlRoot` -> `Root` `BxmlVerb` -> `Verb` * Refactor tests for bxml base classes * Add `test_tag.py` to test `<Tag>` Also fixed some bugs in the base models causing test failures * Clean up tests to follow proper pytest convention Removed `lxml` from requirements Added base class tests * Add another test to add verbs during init of root * DX-2901, DX-2908, DX-2918 (#114) * DX-2901 `<PhoneNumber>` BXML Had to fix some bugs with the attributes in the `Verb` class Reordered arguments in the `Tag` class to match `Verb` * DX-2908 Refactor `<SipUri> BXML * DX-2918 Refactor `<Transfer>` BXML * Cleanup and update descriptions in docstrings * Fix WebRTC Utilities Test * import List type for Python < 3.9 https://stackoverflow.com/a/63460173/11633328 * Fix other `List` instances * Missed one `List` * Add `Union` to support multiple types in List * Construct verbs in alphabetical order to satisfy [email protected] tests * Alphabetize attributes dict for python3.7
1 parent cb6e88d commit 5ad427c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+630
-1768
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ target/
6969
#Ipython Notebook
7070
.ipynb_checkpoints
7171

72+
# IntelliJ
73+
.idea
74+
7275
# VS Code
7376
.vscode
7477

bandwidth/model/bxml/bxml.py

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,21 @@
11
"""
22
bxml.py
33
4-
Class that allows user to generate BXML programatically in python
5-
BXML is the parent element
4+
Class that allows user to generate a Bxml document
65
76
@copyright Bandwidth INC
87
"""
8+
from typing import List
99

10-
BXML_TAG = "Bxml"
11-
XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>'
10+
from .root import Root
11+
from .verb import Verb
1212

1313

14-
class Bxml:
14+
class Bxml(Root):
15+
def __init__(self, nested_verbs: List[Verb] = []):
16+
"""Initialize an instance of the <Bxml> root
1517
16-
def __init__(self):
18+
Args:
19+
nested_verbs (list[BxmlVerb], optional): Optional nested verbs to create the model with. Defaults to [].
1720
"""
18-
Creates the Bxml class
19-
"""
20-
self.verbs = []
21-
22-
def add_verb(self, verb):
23-
"""
24-
Adds the Verb to the already existing verbs
25-
26-
:param Verb verb: The Verb to add
27-
"""
28-
self.verbs.append(verb)
29-
30-
def to_bxml(self):
31-
"""
32-
Converts the Bxml class to its XML representation
33-
34-
:rtype str: The XML representation of the Bxml class
35-
"""
36-
xml_string = XML_HEADER
37-
xml_string += '<' + BXML_TAG + '>'
38-
for verb in self.verbs:
39-
xml_string += verb.to_bxml()
40-
xml_string += '</' + BXML_TAG + '>'
41-
42-
return xml_string
21+
super().__init__(tag="Bxml", nested_verbs=nested_verbs)

bandwidth/model/bxml/response.py

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,21 @@
11
"""
22
response.py
33
4-
Class that allows user to generate BXML programatically in python
4+
Class that allows user to generate a Response document
55
66
@copyright Bandwidth INC
77
"""
8+
from typing import List
89

9-
RESPONSE_TAG = "Response"
10-
XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>'
10+
from .root import Root
11+
from .verb import Verb
1112

1213

13-
class Response:
14+
class Response(Root):
15+
def __init__(self, nested_verbs: List[Verb] = []):
16+
"""Initialize an instance of the <Response> root
1417
15-
def __init__(self):
18+
Args:
19+
nested_verbs (list[BxmlVerb], optional): Optional nested verbs to create the model with. Defaults to [].
1620
"""
17-
Creates the Response class
18-
"""
19-
self.verbs = []
20-
21-
def add_verb(self, verb):
22-
"""
23-
Adds the Verb to the already existing verbs
24-
25-
:param Verb verb: The Verb to add
26-
"""
27-
self.verbs.append(verb)
28-
29-
def to_bxml(self):
30-
"""
31-
Converts the Response class to its XML representation
32-
33-
:rtype str: The XML representation of the Response class
34-
"""
35-
xml_string = XML_HEADER
36-
xml_string += '<' + RESPONSE_TAG + '>'
37-
for verb in self.verbs:
38-
xml_string += verb.to_bxml()
39-
xml_string += '</' + RESPONSE_TAG + '>'
40-
41-
return xml_string
21+
super().__init__(tag="Response", nested_verbs=nested_verbs)

bandwidth/model/bxml/root.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
root.py
3+
4+
Defines the base verb class for all BXML roots
5+
6+
@copyright Bandwidth INC
7+
"""
8+
from typing import List
9+
import xml.etree.ElementTree as ET
10+
11+
from bandwidth.model.bxml.verb import Verb
12+
13+
14+
class Root:
15+
"""Base class for BXML roots
16+
"""
17+
18+
def __init__(self, tag: str, nested_verbs: List[Verb] = None):
19+
"""Initialize instance of class
20+
21+
Args:
22+
tag (str): The XML element name
23+
nested_verbs (list[BxmlVerb], optional): List of nested BXML verbs. Defaults to None.
24+
"""
25+
self._tag = tag
26+
self._nested_verbs = nested_verbs
27+
if not self._nested_verbs:
28+
self._nested_verbs = []
29+
30+
def __len__(self) -> int:
31+
"""Override default len method. Returns length of _nested_verbs array
32+
33+
Returns:
34+
int: Length of self._nested_verbs
35+
"""
36+
return len(self._nested_verbs)
37+
38+
def __getitem__(self, position: int) -> Verb:
39+
"""Override default getitem method. Makes the object iterable.
40+
41+
Args:
42+
position (int): Desired self._nested_verbs list position
43+
44+
Returns:
45+
BxmlVerb: Desired BXML verb
46+
"""
47+
return self._nested_verbs[position]
48+
49+
def _generate_xml(self) -> ET.Element:
50+
"""Generates an XML dom
51+
52+
Returns:
53+
ET.Element: The XML dom for the verb and its nested verbs
54+
"""
55+
root = ET.Element(self._tag)
56+
if self._nested_verbs:
57+
for verb in self._nested_verbs:
58+
root.append(verb._to_etree_element())
59+
dom = ET.ElementTree(root)
60+
return dom
61+
62+
def add_verb(self, verb: Verb) -> None:
63+
"""Add a verb to the object's nested_verbs array
64+
65+
Args:
66+
verb (BxmlVerb): BXML verb to nest within the parent. Becomes a child xml element.
67+
"""
68+
self._nested_verbs.append(verb)
69+
70+
def to_bxml(self) -> str:
71+
"""Return the serialized BXML string
72+
73+
Returns:
74+
str: Serialized BXML string
75+
"""
76+
xml_document = self._generate_xml()
77+
return ET.tostring(xml_document._root, encoding='utf8', method='xml').decode("utf8")
78+

bandwidth/model/bxml/verb.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""
2+
verb.py
3+
4+
Defines the base verb class for all BXML verbs
5+
6+
@copyright Bandwidth INC
7+
"""
8+
from __future__ import annotations
9+
import xml.etree.ElementTree as ET
10+
11+
12+
class Verb:
13+
"""Base class for BXML verbs
14+
"""
15+
16+
def __init__(self, tag: str, content: str = None, attributes: dict = None, nested_verbs: list[BxmlVerb] = None):
17+
"""Initialize the verb model
18+
19+
Args:
20+
tag (str): Name of the XML element
21+
content (str, optional): XML element content. Defaults to None.
22+
attributes (dict, optional): XML element attributes. Defaults to None.
23+
nested_verbs (list[BxmlVerb], optional): XML element children. Defaults to None.
24+
"""
25+
self._tag = tag
26+
self._content = content
27+
self._attributes = attributes
28+
self._nested_verbs = nested_verbs
29+
if not self._nested_verbs:
30+
self._nested_verbs = []
31+
32+
def __len__(self) -> int:
33+
"""Override default len method. Returns length of _nested_verbs array
34+
35+
Returns:
36+
int: Length of self._nested_verbs
37+
"""
38+
return len(self._nested_verbs)
39+
40+
def __getitem__(self, position) -> Verb:
41+
"""Override default getitem method. Makes the object iterable.
42+
43+
Args:
44+
position (int): Desired self._nested_verbs list position
45+
46+
Returns:
47+
BxmlVerb: Desired BXML verb
48+
"""
49+
return self._nested_verbs[position]
50+
51+
def _set_attributes(self, root: ET.Element):
52+
"""Set XML attributes on an Element
53+
54+
Args:
55+
root (ET.Element): XML Element to add attributes to
56+
"""
57+
if self._attributes:
58+
for key, value in self._attributes.items():
59+
if value is not None:
60+
root.set(key, value)
61+
62+
def _to_etree_element(self) -> ET.Element:
63+
"""Generate an ET.Element object from a Verb Object
64+
65+
Returns:
66+
ET.Element: ET.Element representation of Verb
67+
"""
68+
root = ET.Element(self._tag)
69+
if self._content:
70+
root.text = self._content
71+
self._set_attributes(root)
72+
if self._nested_verbs:
73+
for verb in self._nested_verbs:
74+
root.append(verb._to_etree_element())
75+
return root
76+
77+
def _generate_xml(self) -> ET.ElementTree:
78+
"""Generates an XML dom
79+
80+
Returns:
81+
ET.Element: The XML dom for the verb and its nested verbs
82+
"""
83+
root = ET.Element(self._tag)
84+
if self._content:
85+
root.text = self._content
86+
self._set_attributes(root)
87+
if self._nested_verbs:
88+
for verb in self._nested_verbs:
89+
root.append(verb._to_etree_element())
90+
dom = ET.ElementTree(root)
91+
return dom
92+
93+
def add_verb(self, verb) -> None:
94+
"""Add a verb to the object's nested_verbs array
95+
96+
Args:
97+
verb (BxmlVerb): BXML verb to nest within the parent. Becomes a child xml element.
98+
"""
99+
self._nested_verbs.append(verb)
100+
101+
def to_bxml(self) -> str:
102+
"""Return the serialized BXML string
103+
104+
Returns:
105+
str: Serialized BXML string
106+
"""
107+
xml_document = self._generate_xml()
108+
return ET.tostring(xml_document._root).decode('utf8')
Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,4 @@
1-
from .hangup import Hangup
2-
from .send_dtmf import SendDtmf
3-
from .gather import Gather
4-
from .pause import Pause
51
from .phone_number import PhoneNumber
6-
from .redirect import Redirect
7-
from .speak_sentence import SpeakSentence
8-
from .transfer import Transfer
9-
from .play_audio import PlayAudio
10-
from .forward import Forward
11-
from .record import Record
12-
from .pause_recording import PauseRecording
13-
from .resume_recording import ResumeRecording
14-
from .stop_recording import StopRecording
15-
from .start_recording import StartRecording
16-
from .conference import Conference
17-
from .bridge import Bridge
18-
from .ring import Ring
19-
from .stop_gather import StopGather
20-
from .start_gather import StartGather
21-
from .tag import Tag
222
from .sip_uri import SipUri
23-
from .start_stream import StartStream
24-
from .stop_stream import StopStream
3+
from .tag import Tag
4+
from .transfer import Transfer

bandwidth/model/bxml/verbs/base_verb.py

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)