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
|
||||
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'
|
||||
SiteComponent='SITE'
|
||||
AuthComponent='API_AUTH'
|
||||
|
@ -18,16 +22,33 @@ class Py3CXEnvironmentVars:
|
|||
#Base class for exceptions means one can 'except Py3CXException'
|
||||
#TODO: should this instead be a subclass of Py3CX? (except Py3CX.Exception?)
|
||||
class Py3CXException(Exception):
|
||||
"""Base Py3CX Exception class"""
|
||||
pass
|
||||
class APIError(Py3CXException):
|
||||
"""Exception class used in event of an error being returned by the API"""
|
||||
pass
|
||||
class ValidationError(Py3CXException):
|
||||
"""Exception class used in event of a validation error internal to the class"""
|
||||
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
|
||||
class Request:
|
||||
"""Internal-use wrapper around Python-Requests"""
|
||||
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
|
||||
self.uri=uri
|
||||
self.base="%s/api/{}" % uri
|
||||
|
@ -36,7 +57,7 @@ class Request:
|
|||
self.sess.verify=verify
|
||||
def __repr__(self):
|
||||
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:
|
||||
if isinstance(method, str):
|
||||
if method is 'GET':
|
||||
|
@ -55,9 +76,9 @@ class Request:
|
|||
except ConnectionError as e:
|
||||
raise APIError("ConnectionError raised when communicating: %s" % str(e))
|
||||
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)
|
||||
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)
|
||||
|
||||
#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
|
||||
class Py3CX:
|
||||
"""Base Py3CX class, containing all functionality for interacting with a 3CX phone system"""
|
||||
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 representing a currently-ongoing or historical call on a 3CX system
|
||||
|
@ -153,7 +175,7 @@ class Py3CX:
|
|||
'Id': self.params})
|
||||
self.refresh()
|
||||
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 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.cancel()
|
||||
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 representing information about the specified extension number.
|
||||
|
@ -373,7 +395,7 @@ class Py3CX:
|
|||
this.since=call['LastChangeStatus']
|
||||
ret.append(this)
|
||||
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 an instance of the Py3CX class, which is the primary entrypoint of this module.
|
||||
|
@ -389,11 +411,14 @@ class Py3CX:
|
|||
self._tcxsystem=None
|
||||
if uri is not None:
|
||||
self.cnf.uri=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)
|
||||
def __repr__(self):
|
||||
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.
|
||||
|
||||
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
|
||||
if password is not None:
|
||||
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={
|
||||
'Username': self.cnf.auth_user,
|
||||
'Password': self.cnf.auth_pwd})
|
||||
|
|
Loading…
Reference in New Issue