Refactor message classes
Message classes allow dynamic attribute access to message fields. Childs get exposed through a dedicated class by attributes too. Message type fields and childs have types that get enforces. None type is allowed too while transitioning.
This commit is contained in:
parent
1752a9151d
commit
cf3c16c66a
@ -20,6 +20,14 @@ mitel\_ommclient2.messages.getppdev module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
mitel\_ommclient2.messages.getppuser module
|
||||
-------------------------------------------
|
||||
|
||||
.. automodule:: mitel_ommclient2.messages.getppuser
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
mitel\_ommclient2.messages.open module
|
||||
--------------------------------------
|
||||
|
||||
|
@ -15,7 +15,7 @@ to establish a transport to the API.
|
||||
|
||||
import mitel_ommclient2
|
||||
|
||||
conn = mitel_ommclient2..connection.Connection("omm.local")
|
||||
conn = mitel_ommclient2.connection.Connection("omm.local")
|
||||
|
||||
To actually connect to the OMM, you need to call :func:`mitel_ommclient2.connection.Connection.connect`.
|
||||
|
||||
@ -45,8 +45,8 @@ You hand over a Request object and receive a response object.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> request = mitel_ommclient2.messages.Ping()
|
||||
>>> r = conn.request(request)
|
||||
>>> m = mitel_ommclient2.messages.Ping()
|
||||
>>> r = conn.request(m)
|
||||
>>> r.name
|
||||
'PingResp'
|
||||
|
||||
|
@ -5,14 +5,13 @@ The API consists of three main message types: request, response and event. They
|
||||
are represented by :class:`mitel_ommclient2.messages.Request`, :class:`mitel_ommclient2.messages.Response`
|
||||
and events aren't supported yet.
|
||||
|
||||
There are several subclasses for each messages type, but they are just just overlays
|
||||
to provide a conveinient interface to message content using attributes.
|
||||
There are several subclasses for each messages type, which provide a conveinient
|
||||
interface to message content using attributes.
|
||||
|
||||
Each message provides three attributes that define the central interfaces:
|
||||
|
||||
* name
|
||||
* attrs
|
||||
* childs
|
||||
For each message you can access each field directly as class attributes.
|
||||
There are two special attributes:
|
||||
* name: returns the message name
|
||||
* childs: allowes you to access childs by the child name as class attributes
|
||||
|
||||
Using messages
|
||||
--------------
|
||||
@ -27,27 +26,20 @@ and hand it over to :func:`mitel_ommclient2.client.OMMClient2.request` or
|
||||
|
||||
my_time = int(time.time())
|
||||
|
||||
request = mitel_ommclient2.messages.Ping(timeStamp=my_time)
|
||||
r = c.request(request)
|
||||
m = mitel_ommclient2.messages.Ping()
|
||||
m.timeStamp = my_time
|
||||
r = c.request(m)
|
||||
|
||||
ping = r.timeStamp - my_time
|
||||
|
||||
Crafting your own messages
|
||||
--------------------------
|
||||
A more complex example
|
||||
----------------------
|
||||
|
||||
It some cases your message class isn't implemented yet. Then you can craft the
|
||||
message yourself.
|
||||
This demonstrates how to access message childs.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
|
||||
my_time = int(time.time())
|
||||
|
||||
request = mitel_ommclient2.messages.Request("Ping")
|
||||
request.attrs = {
|
||||
"timeStamp": my_time,
|
||||
}
|
||||
r = c.request(request)
|
||||
|
||||
ping = r.attrs["timeStamp"] - my_time
|
||||
m = messages.GetAccount()
|
||||
m.id = id
|
||||
r = self.connection.request(m)
|
||||
return r.childs.account[0]
|
||||
|
@ -44,7 +44,11 @@ class OMMClient2:
|
||||
self.connection.connect()
|
||||
|
||||
# Login
|
||||
r = self.connection.request(messages.Open(self._username, self._password, UserDeviceSyncClient=self._ommsync))
|
||||
m = messages.Open()
|
||||
m.username = self._username
|
||||
m.password = self._password
|
||||
m.UserDeviceSyncClient = self._ommsync
|
||||
r = self.connection.request(m)
|
||||
r.raise_on_error()
|
||||
|
||||
def get_account(self, id):
|
||||
@ -54,11 +58,13 @@ class OMMClient2:
|
||||
:param id: User id
|
||||
"""
|
||||
|
||||
r = self.connection.request(messages.GetAccount(id))
|
||||
m = messages.GetAccount()
|
||||
m.id = id
|
||||
r = self.connection.request(m)
|
||||
r.raise_on_error()
|
||||
if r.account is None:
|
||||
if r.childs.account is None:
|
||||
return None
|
||||
return r.account[0]
|
||||
return r.childs.account[0]
|
||||
|
||||
def get_device(self, ppn):
|
||||
"""
|
||||
@ -66,11 +72,14 @@ class OMMClient2:
|
||||
|
||||
:param ppn: Device id
|
||||
"""
|
||||
r = self.connection.request(messages.GetPPDev(ppn))
|
||||
|
||||
m = messages.GetPPDev()
|
||||
m.ppn = ppn
|
||||
r = self.connection.request(m)
|
||||
r.raise_on_error()
|
||||
if r.pp is None:
|
||||
if r.childs.pp is None:
|
||||
return None
|
||||
return r.pp[0]
|
||||
return r.childs.pp[0]
|
||||
|
||||
def get_devices(self):
|
||||
"""
|
||||
@ -78,7 +87,10 @@ class OMMClient2:
|
||||
"""
|
||||
next_ppn = 0
|
||||
while True:
|
||||
r = self.connection.request(messages.GetPPDev(next_ppn, maxRecords=20))
|
||||
m = messages.GetPPDev()
|
||||
m.ppn = next_ppn
|
||||
m.maxRecords = 20
|
||||
r = self.connection.request(m)
|
||||
try:
|
||||
r.raise_on_error()
|
||||
except exceptions.ENoEnt:
|
||||
@ -86,7 +98,7 @@ class OMMClient2:
|
||||
break
|
||||
|
||||
# Output all found devices
|
||||
for pp in r.pp:
|
||||
for pp in r.childs.pp:
|
||||
yield pp
|
||||
|
||||
# Determine next possible ppn
|
||||
@ -98,11 +110,13 @@ class OMMClient2:
|
||||
|
||||
:param uid: User id
|
||||
"""
|
||||
r = self.connection.request(messages.GetPPUser(uid))
|
||||
m = messages.GetPPUser()
|
||||
m.uid = uid
|
||||
r = self.connection.request(m)
|
||||
r.raise_on_error()
|
||||
if r.user is None:
|
||||
if r.childs.user is None:
|
||||
return None
|
||||
return r.user[0]
|
||||
return r.childs.user[0]
|
||||
|
||||
def get_users(self):
|
||||
"""
|
||||
@ -110,7 +124,10 @@ class OMMClient2:
|
||||
"""
|
||||
next_uid = 0
|
||||
while True:
|
||||
r = self.connection.request(messages.GetPPUser(next_uid, maxRecords=20))
|
||||
m = messages.GetPPUser()
|
||||
m.uid = next_uid
|
||||
m.maxRecords = 20
|
||||
r = self.connection.request(m)
|
||||
try:
|
||||
r.raise_on_error()
|
||||
except exceptions.ENoEnt:
|
||||
@ -118,7 +135,7 @@ class OMMClient2:
|
||||
break
|
||||
|
||||
# Output all found devices
|
||||
for user in r.user:
|
||||
for user in r.childs.user:
|
||||
yield user
|
||||
|
||||
# Determine next possible ppn
|
||||
|
@ -5,45 +5,98 @@ from xml.dom.minidom import getDOMImplementation, parseString
|
||||
from ..exceptions import exception_classes, OMResponseException
|
||||
|
||||
|
||||
class Request:
|
||||
class Message:
|
||||
"""
|
||||
Request message class
|
||||
|
||||
:param name: Name of the message
|
||||
:param seq: Unique sequence number to associate responses
|
||||
|
||||
Usage::
|
||||
>>> req = Request("Ping")
|
||||
"""
|
||||
|
||||
def __init__(self, name, seq=None):
|
||||
self.name = name
|
||||
self.attrs = {}
|
||||
self.childs = {}
|
||||
|
||||
if seq is not None:
|
||||
self.attrs["seq"] = seq
|
||||
|
||||
@property
|
||||
def seq(self):
|
||||
return self.attrs.get("seq")
|
||||
|
||||
@seq.setter
|
||||
def seq(self, seq):
|
||||
self.attrs["seq"] = seq
|
||||
|
||||
class Response:
|
||||
"""
|
||||
Response message class
|
||||
Base message class
|
||||
|
||||
:param name: Name of the message
|
||||
:param attrs: Message attributes
|
||||
:param childs: Message children
|
||||
"""
|
||||
def __init__(self, name, attrs={}, childs={}):
|
||||
|
||||
# Fields defined by the base type class
|
||||
BASE_FIELDS = {}
|
||||
# Fields defined by subclasses
|
||||
FIELDS = {}
|
||||
# Child types
|
||||
CHILDS = {}
|
||||
# Fields dicts consist of the field name as name and the field type as value
|
||||
# Use None if the field type is unknown, any type is allowed then
|
||||
|
||||
|
||||
class Childs:
|
||||
"""
|
||||
Contains message childs
|
||||
"""
|
||||
|
||||
CHILDS = {}
|
||||
|
||||
def __init__(self, child_types, child_dict):
|
||||
self.CHILDS = child_types
|
||||
self._child_dict = child_dict
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self.CHILDS.keys():
|
||||
return self._child_dict.get(name)
|
||||
else:
|
||||
raise AttributeError()
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in self.CHILDS.keys():
|
||||
if self.CHILDS[name] is not None and type(value) != self.CHILDS[name]:
|
||||
raise TypeError()
|
||||
self._child_dict[name] = value
|
||||
else:
|
||||
object.__setattr__(self, name, value)
|
||||
|
||||
|
||||
def __init__(self, name=None, attrs={}, childs={}):
|
||||
self.name = name
|
||||
self.attrs = attrs
|
||||
self.childs = childs
|
||||
if not self.name:
|
||||
self.name = self.__class__.__name__
|
||||
self._attrs = attrs
|
||||
self._childs = childs
|
||||
self.childs = self.Childs(self.CHILDS, self._childs)
|
||||
|
||||
def __getattr__(self, name):
|
||||
fields = self.FIELDS | self.BASE_FIELDS
|
||||
if name in fields.keys():
|
||||
return self._attrs.get(name)
|
||||
else:
|
||||
raise AttributeError()
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
fields = self.FIELDS | self.BASE_FIELDS
|
||||
if name in fields.keys():
|
||||
if fields[name] is not None and type(value) != fields[name]:
|
||||
raise TypeError()
|
||||
self._attrs[name] = value
|
||||
else:
|
||||
object.__setattr__(self, name, value)
|
||||
|
||||
|
||||
class Request(Message):
|
||||
"""
|
||||
Request message type class
|
||||
"""
|
||||
|
||||
BASE_FIELDS = {
|
||||
"seq": int,
|
||||
}
|
||||
|
||||
|
||||
class Response(Message):
|
||||
"""
|
||||
Response message type class
|
||||
"""
|
||||
|
||||
BASE_FIELDS = {
|
||||
"seq": int,
|
||||
"errCode": None,
|
||||
"info": None,
|
||||
"bad": None,
|
||||
"maxLen": None,
|
||||
}
|
||||
|
||||
def raise_on_error(self):
|
||||
"""
|
||||
@ -62,25 +115,17 @@ class Response:
|
||||
if self.errCode is not None:
|
||||
raise exception_classes.get(self.errCode, OMResponseException)(response=self)
|
||||
|
||||
@property
|
||||
def seq(self):
|
||||
return int(self.attrs.get("seq"))
|
||||
|
||||
@property
|
||||
def errCode(self):
|
||||
return self.attrs.get("errCode")
|
||||
REQUEST_TYPES = {}
|
||||
RESPONSE_TYPES = {}
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
return self.attrs.get("info")
|
||||
def request_type(c):
|
||||
REQUEST_TYPES[c.__name__] = c
|
||||
return c
|
||||
|
||||
@property
|
||||
def bad(self):
|
||||
return self.attrs.get("bad")
|
||||
|
||||
@property
|
||||
def maxLen(self):
|
||||
return self.attrs.get("maxLen")
|
||||
def response_type(c):
|
||||
RESPONSE_TYPES[c.__name__] = c
|
||||
return c
|
||||
|
||||
from .getaccount import GetAccount, GetAccountResp
|
||||
from .getppdev import GetPPDev, GetPPDevResp
|
||||
@ -96,11 +141,11 @@ def construct(request):
|
||||
message = impl.createDocument(None, request.name, None)
|
||||
root = message.documentElement
|
||||
|
||||
for k, v in request.attrs.items():
|
||||
for k, v in request._attrs.items():
|
||||
root.setAttribute(str(k), str(v))
|
||||
|
||||
|
||||
for k, v in request.childs.items():
|
||||
for k, v in request._childs.items():
|
||||
child = message.createElement(k)
|
||||
if v is not None:
|
||||
for c_k, c_v in v.items():
|
||||
@ -108,18 +153,6 @@ def construct(request):
|
||||
root.appendChild(child)
|
||||
return root.toxml()
|
||||
|
||||
def _response_type_by_name(name):
|
||||
response_types = [
|
||||
GetAccountResp,
|
||||
GetPPDevResp,
|
||||
GetPPUserResp,
|
||||
PingResp,
|
||||
]
|
||||
|
||||
response_types_dict = {r.__name__: r for r in response_types}
|
||||
|
||||
return response_types_dict.get(name, Response)
|
||||
|
||||
def parse(message):
|
||||
message = parseString(message)
|
||||
root = message.documentElement
|
||||
@ -128,8 +161,14 @@ def parse(message):
|
||||
attrs = {}
|
||||
childs = {}
|
||||
|
||||
response_type = RESPONSE_TYPES.get(name)
|
||||
fields = response_type.FIELDS | response_type.BASE_FIELDS
|
||||
|
||||
for i in range(0, root.attributes.length):
|
||||
item = root.attributes.item(i)
|
||||
if fields.get(item.name) is not None:
|
||||
attrs[item.name] = fields[item.name](item.value)
|
||||
else:
|
||||
attrs[item.name] = item.value
|
||||
|
||||
child = root.firstChild
|
||||
@ -148,4 +187,4 @@ def parse(message):
|
||||
child = child.nextSibling
|
||||
|
||||
|
||||
return _response_type_by_name(name)(name, attrs, childs)
|
||||
return response_type(name, attrs, childs)
|
||||
|
@ -1,27 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from . import Request, Response
|
||||
from . import Request, Response, request_type, response_type
|
||||
|
||||
|
||||
@request_type
|
||||
class GetAccount(Request):
|
||||
def __init__(self, id, maxRecords=None, **kwargs):
|
||||
super().__init__("GetAccount", **kwargs)
|
||||
|
||||
self.attrs["id"] = id
|
||||
|
||||
if maxRecords is not None:
|
||||
self.attrs["maxRecords"] = maxRecords
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.attrs.get("id")
|
||||
|
||||
@property
|
||||
def maxRecords(self):
|
||||
return self.attrs.get("maxRecords")
|
||||
FIELDS = {
|
||||
"id": int,
|
||||
"maxRecords": int,
|
||||
}
|
||||
|
||||
|
||||
@response_type
|
||||
class GetAccountResp(Response):
|
||||
@property
|
||||
def account(self):
|
||||
return self.childs.get("account")
|
||||
CHILDS = {
|
||||
"account": None,
|
||||
}
|
||||
|
@ -1,27 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from . import Request, Response
|
||||
from . import Request, Response, request_type, response_type
|
||||
|
||||
|
||||
@request_type
|
||||
class GetPPDev(Request):
|
||||
def __init__(self, ppn, maxRecords=None, **kwargs):
|
||||
super().__init__("GetPPDev", **kwargs)
|
||||
|
||||
self.attrs["ppn"] = ppn
|
||||
|
||||
if maxRecords is not None:
|
||||
self.attrs["maxRecords"] = maxRecords
|
||||
|
||||
@property
|
||||
def ppn(self):
|
||||
return self.attrs.get("ppn")
|
||||
|
||||
@property
|
||||
def maxRecords(self):
|
||||
return self.attrs.get("maxRecords")
|
||||
FIELDS = {
|
||||
"ppn": int,
|
||||
"maxRecords": int,
|
||||
}
|
||||
|
||||
|
||||
@response_type
|
||||
class GetPPDevResp(Response):
|
||||
@property
|
||||
def pp(self):
|
||||
return self.childs.get("pp")
|
||||
CHILDS = {
|
||||
"pp": None,
|
||||
}
|
||||
|
@ -1,27 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from . import Request, Response
|
||||
from . import Request, Response, request_type, response_type
|
||||
|
||||
|
||||
@request_type
|
||||
class GetPPUser(Request):
|
||||
def __init__(self, uid, maxRecords=None, **kwargs):
|
||||
super().__init__("GetPPUser", **kwargs)
|
||||
|
||||
self.attrs["uid"] = uid
|
||||
|
||||
if maxRecords is not None:
|
||||
self.attrs["maxRecords"] = maxRecords
|
||||
|
||||
@property
|
||||
def ppn(self):
|
||||
return self.attrs.get("ppn")
|
||||
|
||||
@property
|
||||
def maxRecords(self):
|
||||
return self.attrs.get("maxRecords")
|
||||
FIELDS = {
|
||||
"uid": int,
|
||||
"maxRecords": int,
|
||||
}
|
||||
|
||||
|
||||
@response_type
|
||||
class GetPPUserResp(Response):
|
||||
@property
|
||||
def user(self):
|
||||
return self.childs.get("user")
|
||||
CHILDS = {
|
||||
"user": None,
|
||||
}
|
||||
|
@ -1,55 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from . import Request, Response
|
||||
from . import Request, Response, request_type, response_type
|
||||
|
||||
|
||||
@request_type
|
||||
class Open(Request):
|
||||
"""
|
||||
Authenticate Message
|
||||
FIELDS = {
|
||||
"username": None,
|
||||
"password": None,
|
||||
"UserDeviceSyncClient": None,
|
||||
}
|
||||
|
||||
Needs to be the first message on a new connection.
|
||||
|
||||
:param username: Username
|
||||
:param password: Password
|
||||
:param UserDeviceSyncClient: If True login as OMM-Sync client. Some operations in OMM-Sync mode might lead to destroy DECT paring.
|
||||
"""
|
||||
def __init__(self, username, password, UserDeviceSyncClient=False, **kwargs):
|
||||
super().__init__("Open", **kwargs)
|
||||
|
||||
self.attrs["username"] = username
|
||||
self.attrs["password"] = password
|
||||
if UserDeviceSyncClient:
|
||||
self.attrs["UserDeviceSyncClient"] = "true"
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
return self.attrs.get("username")
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
return self.attrs.get("password")
|
||||
|
||||
@property
|
||||
def UserDeviceSyncClient(self):
|
||||
return self.attrs.get("UserDeviceSyncClient")
|
||||
|
||||
@response_type
|
||||
class OpenResp(Response):
|
||||
@property
|
||||
def protocolVersion(self):
|
||||
return self.attrs.get("protocolVersion")
|
||||
|
||||
@property
|
||||
def minPPSwVersion1(self):
|
||||
return self.attrs.get("minPPSwVersion1")
|
||||
|
||||
@property
|
||||
def minPPSwVersion2(self):
|
||||
return self.attrs.get("minPPSwVersion2")
|
||||
|
||||
@property
|
||||
def ommStbState(self):
|
||||
return self.attrs.get("ommStbState")
|
||||
|
||||
@property
|
||||
def publicKey(self):
|
||||
return self.attrs.get("publicKey")
|
||||
FIELDS = {
|
||||
"protocolVersion": None,
|
||||
"minPPSwVersion1": None,
|
||||
"minPPSwVersion2": None,
|
||||
"ommStbState": None,
|
||||
"publicKey": None,
|
||||
}
|
||||
|
@ -1,20 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from . import Request, Response
|
||||
from . import Request, Response, request_type, response_type
|
||||
|
||||
|
||||
@request_type
|
||||
class Ping(Request):
|
||||
def __init__(self, timeStamp=None, **kwargs):
|
||||
super().__init__("Ping", **kwargs)
|
||||
FIELDS = {
|
||||
"timeStamp": int,
|
||||
}
|
||||
|
||||
if timeStamp is not None:
|
||||
self.attrs["timeStamp"] = timeStamp
|
||||
|
||||
@property
|
||||
def timeStamp(self):
|
||||
return self.attrs.get("timeStamp")
|
||||
|
||||
@response_type
|
||||
class PingResp(Response):
|
||||
@property
|
||||
def timeStamp(self):
|
||||
return self.attrs.get("timeStamp")
|
||||
FIELDS = {
|
||||
"timeStamp": int,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user