more inline docs, added some better error handling
This commit is contained in:
parent
c4869200db
commit
03203f6555
|
@ -2,6 +2,10 @@ import typing
|
||||||
|
|
||||||
#This became a class so we can combine envvar initialisation with cred storage
|
#This became a class so we can combine envvar initialisation with cred storage
|
||||||
class Py3CXEnvironmentVars:
|
class Py3CXEnvironmentVars:
|
||||||
|
"""Class to initialise module configuration from environment variables
|
||||||
|
|
||||||
|
Class to initialise module configuration from environment variables, but doubles as configuration storage for explicit credentials
|
||||||
|
"""
|
||||||
Prefix='TCX'
|
Prefix='TCX'
|
||||||
SiteComponent='SITE'
|
SiteComponent='SITE'
|
||||||
AuthComponent='API_AUTH'
|
AuthComponent='API_AUTH'
|
||||||
|
@ -18,16 +22,33 @@ class Py3CXEnvironmentVars:
|
||||||
#Base class for exceptions means one can 'except Py3CXException'
|
#Base class for exceptions means one can 'except Py3CXException'
|
||||||
#TODO: should this instead be a subclass of Py3CX? (except Py3CX.Exception?)
|
#TODO: should this instead be a subclass of Py3CX? (except Py3CX.Exception?)
|
||||||
class Py3CXException(Exception):
|
class Py3CXException(Exception):
|
||||||
|
"""Base Py3CX Exception class"""
|
||||||
pass
|
pass
|
||||||
class APIError(Py3CXException):
|
class APIError(Py3CXException):
|
||||||
|
"""Exception class used in event of an error being returned by the API"""
|
||||||
pass
|
pass
|
||||||
class ValidationError(Py3CXException):
|
class ValidationError(Py3CXException):
|
||||||
|
"""Exception class used in event of a validation error internal to the class"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#Request is a basic wrapper around python-requests to remove the need to always specify the API base uri, and to handle basic result validation on behalf of the caller
|
#Request is a basic wrapper around python-requests to remove the need to always specify the API base uri, and to handle basic result validation on behalf of the caller
|
||||||
class Request:
|
class Request:
|
||||||
|
"""Internal-use wrapper around Python-Requests"""
|
||||||
from requests import Response, ConnectionError, exceptions
|
from requests import Response, ConnectionError, exceptions
|
||||||
def __init__(self, uri, verify):
|
def __init__(self, uri : str, verify : bool):
|
||||||
|
"""Initialises a Request object
|
||||||
|
|
||||||
|
Initialises a Python-Requests Session object with decorative attributes and wrappers
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
---
|
||||||
|
uri: Required, string representing the FQDN where the API lives
|
||||||
|
verify: Required, boolean representing whether to verify the TLS trust chain during connections
|
||||||
|
|
||||||
|
>>> rq=Request('http://myinstance.3cx.com',False)
|
||||||
|
>>> rq
|
||||||
|
<Request [http://myinstance.3cx.com,False]>
|
||||||
|
"""
|
||||||
from requests import Session
|
from requests import Session
|
||||||
self.uri=uri
|
self.uri=uri
|
||||||
self.base="%s/api/{}" % uri
|
self.base="%s/api/{}" % uri
|
||||||
|
@ -36,7 +57,7 @@ class Request:
|
||||||
self.sess.verify=verify
|
self.sess.verify=verify
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Request [%s,v%s]>' % (self.base,self.sess.verify)
|
return '<Request [%s,v%s]>' % (self.base,self.sess.verify)
|
||||||
def _action(self, api, method, params, expect=200) -> 'Response':
|
def _action(self, api : str, method, params, expect=200) -> 'Response':
|
||||||
try:
|
try:
|
||||||
if isinstance(method, str):
|
if isinstance(method, str):
|
||||||
if method is 'GET':
|
if method is 'GET':
|
||||||
|
@ -55,9 +76,9 @@ class Request:
|
||||||
except ConnectionError as e:
|
except ConnectionError as e:
|
||||||
raise APIError("ConnectionError raised when communicating: %s" % str(e))
|
raise APIError("ConnectionError raised when communicating: %s" % str(e))
|
||||||
return resp
|
return resp
|
||||||
def get(self, api, params=None, expect=200) -> 'Response':
|
def get(self, api : str, params=None, expect : int =200) -> 'Response':
|
||||||
return self._action(api, self.sess.get, params=params, expect=expect)
|
return self._action(api, self.sess.get, params=params, expect=expect)
|
||||||
def post(self, api, params, expect=200) -> 'Response':
|
def post(self, api : str, params, expect : int =200) -> 'Response':
|
||||||
return self._action(api, self.sess.post, params=params, expect=expect)
|
return self._action(api, self.sess.post, params=params, expect=expect)
|
||||||
|
|
||||||
#BasicObject is literally just an object - hacky way of making a variable addressable via var.prop rather than var['prop']
|
#BasicObject is literally just an object - hacky way of making a variable addressable via var.prop rather than var['prop']
|
||||||
|
@ -97,8 +118,9 @@ class TransactionalObject(APIObject):
|
||||||
|
|
||||||
#Main logic goes here
|
#Main logic goes here
|
||||||
class Py3CX:
|
class Py3CX:
|
||||||
|
"""Base Py3CX class, containing all functionality for interacting with a 3CX phone system"""
|
||||||
class _Call(ReadOnlyObject):
|
class _Call(ReadOnlyObject):
|
||||||
def __init__(self, tcx, callid):
|
def __init__(self, tcx : 'Py3CX', callid : int):
|
||||||
"""Returns a Py3CX.Call object
|
"""Returns a Py3CX.Call object
|
||||||
|
|
||||||
Returns a Py3CX.Call object representing a currently-ongoing or historical call on a 3CX system
|
Returns a Py3CX.Call object representing a currently-ongoing or historical call on a 3CX system
|
||||||
|
@ -153,7 +175,7 @@ class Py3CX:
|
||||||
'Id': self.params})
|
'Id': self.params})
|
||||||
self.refresh()
|
self.refresh()
|
||||||
class _User(TransactionalObject):
|
class _User(TransactionalObject):
|
||||||
def __init__(self, parent, params):
|
def __init__(self, parent : 'Py3CX', params : str):
|
||||||
"""Returns a Py3CX.User object
|
"""Returns a Py3CX.User object
|
||||||
|
|
||||||
Returns a Py3CX.User object which represents a given user account (Extension) on a 3CX system, including more advanced attributes.
|
Returns a Py3CX.User object which represents a given user account (Extension) on a 3CX system, including more advanced attributes.
|
||||||
|
@ -192,7 +214,7 @@ class Py3CX:
|
||||||
self.extension=Py3CX._Extension(self.tcx, self.params)
|
self.extension=Py3CX._Extension(self.tcx, self.params)
|
||||||
self.cancel()
|
self.cancel()
|
||||||
class _Extension(ReadOnlyObject):
|
class _Extension(ReadOnlyObject):
|
||||||
def __init__(self, parent, params, populate=True):
|
def __init__(self, parent : 'Py3CX', params : str, populate : bool =True):
|
||||||
"""Returns a Py3CX.Extension object
|
"""Returns a Py3CX.Extension object
|
||||||
|
|
||||||
Returns a Py3CX.Extension object representing information about the specified extension number.
|
Returns a Py3CX.Extension object representing information about the specified extension number.
|
||||||
|
@ -373,7 +395,7 @@ class Py3CX:
|
||||||
this.since=call['LastChangeStatus']
|
this.since=call['LastChangeStatus']
|
||||||
ret.append(this)
|
ret.append(this)
|
||||||
return ret
|
return ret
|
||||||
def __init__(self, uri=None, tls_verify=True):
|
def __init__(self, uri=None, tls_verify : bool =True):
|
||||||
"""Returns a Py3CX class object instance
|
"""Returns a Py3CX class object instance
|
||||||
|
|
||||||
Returns an instance of the Py3CX class, which is the primary entrypoint of this module.
|
Returns an instance of the Py3CX class, which is the primary entrypoint of this module.
|
||||||
|
@ -389,11 +411,14 @@ class Py3CX:
|
||||||
self._tcxsystem=None
|
self._tcxsystem=None
|
||||||
if uri is not None:
|
if uri is not None:
|
||||||
self.cnf.uri=uri
|
self.cnf.uri=uri
|
||||||
assert (self.cnf.uri is not None and self.cnf.uri.startswith('http')), "No or invalid URI specified, please provide via uri= or by setting environment variable %s" % self.cnf.URI
|
try:
|
||||||
|
assert (self.cnf.uri is not None and self.cnf.uri.startswith('http')), "No or invalid URI specified, please provide via uri= or by setting environment variable %s" % self.cnf.URI
|
||||||
|
except AssertionError as e:
|
||||||
|
raise ValidationError(str(e))
|
||||||
self.rq=Request(uri=self.cnf.uri, verify=tls_verify)
|
self.rq=Request(uri=self.cnf.uri, verify=tls_verify)
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Py3CX [%s@%s]>' % (self.cnf.auth_user, self.cnf.uri)
|
return '<Py3CX [%s@%s]>' % (self.cnf.auth_user, self.cnf.uri)
|
||||||
def authenticate(self, username=None, password=None):
|
def authenticate(self, username=None, password=None) -> None:
|
||||||
"""Authenticates against the given API, optionally using explicit credentials.
|
"""Authenticates against the given API, optionally using explicit credentials.
|
||||||
|
|
||||||
Performs a POST request to the 'login' endpoint of the current API server, using either credentials gleaned through the execution environment, or explicitly specified during the function invocation.
|
Performs a POST request to the 'login' endpoint of the current API server, using either credentials gleaned through the execution environment, or explicitly specified during the function invocation.
|
||||||
|
@ -406,7 +431,10 @@ class Py3CX:
|
||||||
self.cnf.auth_user=username
|
self.cnf.auth_user=username
|
||||||
if password is not None:
|
if password is not None:
|
||||||
self.cnf.auth_pwd=password
|
self.cnf.auth_pwd=password
|
||||||
assert (self.cnf.auth_user is not None and self.cnf.auth_pwd is not None), "Authentication information needed. Please pass username= and password= or define environment variables %s and %s" % (self.cnf.AUTH_USER, self.cnf.AUTH_PWD)
|
try:
|
||||||
|
assert (None not in (self.cnf.auth_user, self.cnf.auth_pwd))
|
||||||
|
except AssertionError as e:
|
||||||
|
raise ValidationError(str(e))
|
||||||
rs=self.rq.post('login', params={
|
rs=self.rq.post('login', params={
|
||||||
'Username': self.cnf.auth_user,
|
'Username': self.cnf.auth_user,
|
||||||
'Password': self.cnf.auth_pwd})
|
'Password': self.cnf.auth_pwd})
|
||||||
|
|
Loading…
Reference in New Issue