Commit c187a7b6 authored by okhin's avatar okhin 🚴

Lot of code, the event_handling looks better. Committing transatcion in SQLite...

Lot of code, the event_handling looks better. Committing transatcion in SQLite is - obviously - a good thing
parent c796e765
Pipeline #19 skipped
...@@ -31,7 +31,7 @@ class PiphoneJSONEncoder(json.JSONEncoder): ...@@ -31,7 +31,7 @@ class PiphoneJSONEncoder(json.JSONEncoder):
, 'history': obj.history , 'history': obj.history
, 'owner': obj.owner } , 'owner': obj.owner }
else: else:
return json.JSONEncoder.default(self, o) return json.JSONEncoder.default(self, obj)
def sanitize_phonenumber(number): def sanitize_phonenumber(number):
""" """
...@@ -52,19 +52,21 @@ class Call(object): ...@@ -52,19 +52,21 @@ class Call(object):
This Class is used to manage operatiosn on a call, to print it and dump it. This Class is used to manage operatiosn on a call, to print it and dump it.
""" """
history = [] history = []
actions = {'Created': 'call_caller'} actions = {'Created': 'call_caller'
, 'ChannelStateChange': 'change'}
def __init__(self, caller, callee, owner, callid=None, db=None): def __init__(self, caller, callee, owner, callid=None, db=None):
try: try:
self.caller = caller self.caller = caller
self.callee = callee self.callee = callee
self.owner = owner self.owner = owner
if callid == None: if callid == None:
self.id = uuid.uuid4() self.id = str(uuid.uuid4())
self.db = db
self.event_handler({'type': 'Created', 'timestamp': datetime.datetime.now().isoformat()})
else: else:
self.id = callid self.id = callid
self.db = db
self.change(('Created', datetime.datetime.now().isoformat(),))
except Exception as e: except Exception as e:
logging.debug("Exception catched: {}".format(e,))
raise e raise e
def url(self): def url(self):
...@@ -72,41 +74,55 @@ class Call(object): ...@@ -72,41 +74,55 @@ class Call(object):
@classmethod @classmethod
def load(cls, callid, db): def load(cls, callid, db):
results = db.execute('SELECT caller, callee, owner, callid, history FROM calls WHERE callid = ?;', (callid,)) logging.debug("Loading call {}".format(callid,))
try: try:
results = db.execute('SELECT caller, callee, owner, callid, history FROM calls WHERE callid = ?;', (callid,))
result = results.fetchone() result = results.fetchone()
object = cls(result[0], result[1], result[2]) assert len(result) == 5
object.id = result[3] object = cls(result[0], result[1], result[2], result[3])
object.db = db
object.history = json.loads(result[4]) object.history = json.loads(result[4])
object.db = db
return object return object
except: except Exception as e:
return None logging.debug("Exception catched: {}".format(e,))
raise e
def change(self, new_state): def update(self, new_state):
''' '''
Let's change the state of the call. new_state is a tuple in the form (newstate, timestamp,) Let's update the state of the call. new_state is a tuple in the form (newstate, timestamp,)
''' '''
logging.debug("Got a new state: {}".format(new_state,)) logging.debug("Got a new state: {}".format(new_state,))
self.history.append(new_state) self.history.append(new_state)
self.save() self.save()
state = new_state[0]
def event_handler(self, event):
'''
There's a new event related to our call
'''
state = event['type']
if state in self.actions: if state in self.actions:
getattr(self, self.actions[state])() getattr(self, self.actions[state])(event=event)
def change(self, event):
'''
Let's change the state of the call
'''
self.update((event['channel']['state'], event['timestamp'],))
def call_caller(self): def call_caller(self, event):
''' '''
Let's do a request on the ARI system Let's do a request on the ARI system
''' '''
results = self.db.execute('SELECT login_ari, pass_ari FROM users WHERE api = ?', (self.owner,)) self.update((event['type'], event['timestamp'],))
try: try:
results = self.db.execute('SELECT login_ari, pass_ari FROM users WHERE api = ?', (self.owner,))
login_ari, token_ari = results.fetchone() login_ari, token_ari = results.fetchone()
payload = {} payload = {}
payload['app'] = 'piphone' payload['app'] = 'piphone'
payload['api_key'] = ':'.join([login_ari, token_ari]) payload['api_key'] = ':'.join([login_ari, token_ari])
payload['endpoint'] = 'SIP/' + sanitize_phonenumber(self.caller) + '@forfait-kwaoo' payload['endpoint'] = 'SIP/' + sanitize_phonenumber(self.caller) + '@forfait-kwaoo'
logging.debug('Preparing to send a request to the ARI with payload {}'.format(payload,)) logging.debug('Preparing to send a request to the ARI with payload {}'.format(payload,))
r = requests.post('http://185.34.33.12:8088/ari/channels', data=payload) r = requests.post('http://185.34.33.12:8088/ari/channels/{}'.format(self.id), data=payload)
logging.debug('Requests sent, got response: {}'.format(r,)) logging.debug('Requests sent, got response: {}'.format(r,))
except Exception as e: except Exception as e:
raise e raise e
...@@ -115,9 +131,15 @@ class Call(object): ...@@ -115,9 +131,15 @@ class Call(object):
''' '''
Save the Call to database. Save the Call to database.
''' '''
logging.debug("Saving call {}: {}".format(self.id, json.dumps(self, cls=PiphoneJSONEncoder)))
try:
self.db.execute('''INSERT OR REPLACE INTO calls (caller, callee, owner, callid, history) self.db.execute('''INSERT OR REPLACE INTO calls (caller, callee, owner, callid, history)
VALUES (?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?) '''
''', (self.caller, self.callee, self.owner, self.id, json.dumps(self.history))) , (self.caller, self.callee, self.owner, self.id, json.dumps(self.history)))
self.db.commit()
except Exception as e:
logging.debug("Got Exception: {}".format(e,))
raise e
# We need a decorator to check if our query is authenticated. # We need a decorator to check if our query is authenticated.
# We will store an API key and SECRET in ur database, the client # We will store an API key and SECRET in ur database, the client
...@@ -197,7 +219,7 @@ class Server(ServerAdapter): ...@@ -197,7 +219,7 @@ class Server(ServerAdapter):
self.logger.debug('Not a channel event, skip') self.logger.debug('Not a channel event, skip')
return return
call = Call.load(event['channel']['id'], self.db) call = Call.load(event['channel']['id'], self.db)
await call.change((event['channel']['state'], event['channel']['creationtime'],)) call.event_handler(event)
def run(self, handler): def run(self, handler):
""" """
...@@ -214,30 +236,66 @@ class Server(ServerAdapter): ...@@ -214,30 +236,66 @@ class Server(ServerAdapter):
self.server.shutdown() self.server.shutdown()
self.threads.shutdown(wait=False) self.threads.shutdown(wait=False)
@app.get('/calls/<callid>')
@app.get('/calls/') @app.get('/calls/')
@authenticated @authenticated
def calls(db): def calls(db, callid=None):
""" """
Return the list of calls associated to the connected user. Return the list of calls associated to the connected user.
The call has a status, caller, callee and history (status change+timestamp) The call has a status, caller, callee and history (status change+timestamp)
""" """
logging.debug("/calls/ called") logging.debug("GET {}".format(request.fullpath))
if callid == None:
try:
results = db.execute('SELECT callid FROM calls WHERE owner = ?;', (request.params['api'],)) results = db.execute('SELECT callid FROM calls WHERE owner = ?;', (request.params['api'],))
calls = [] calls = []
for call in results.fetchall(): for row in results.fetchall():
calls.append(Call.load(call[0], db)) call = Call.load(row[0], db)
logging.debug("Call fetched: {}".format(json.dumps(call, cls=PiphoneJSONEncoder)))
calls.append(call)
head = {'call': request.fullpath, 'user': request.params['api'], 'hits': len(calls)} head = {'call': request.fullpath, 'user': request.params['api'], 'hits': len(calls)}
return {'head': head, 'data': calls} return {'head': head, 'data': calls}
except Exception as e:
logging.error("Got exception: {}".format(e,))
abort(500, "Exception")
# We first need to check if we can access the callid we asked for
try:
results = db.execute('SELECT callid FROM calls WHERE owner = ? AND callid = ? ;'
, (request.params['api'], callid,))
rows = results.fetchall()
logging.debug("Found {} results: {}".format(len(rows), rows))
assert len(rows) == 1
call = Call.load(callid, db)
head = {'call': call.url(), 'user': request.params['api'], 'hits': 1}
return {'head': head, 'data': call}
except AssertionError as e:
logging.debg("Not exactly one results found, this is an issue")
logging.error("Unauthorized access to call {} from user {}".format(callid, request.params['api']))
abort(403, "You do not have the authorization to get this call")
except Exception as e:
logging.debug("Exception catched: {}".format(e,))
logging.error("Call not found {}".format(callid,))
abort(404, "This call does not exist")
@app.post('/calls/<callid>/caller/<caller>/callee/<callee>/') @app.post('/calls/')
@app.post('/calls/<callid>')
@authenticated @authenticated
def originate(db, callid, caller, callee): def originate(db, callid=None):
call = Call(caller, callee, request.params['api'], callid, db) logging.debug("POST {}".format(request.fullpath))
try:
if callid is not None:
call = Call(request.params['caller'], request.params['callee'], request.params['api'], callid=request.params['callid'], db=db)
else:
call = Call(request.params['caller'], request.params['callee'], request.params['api'], db=db)
logging.debug("Originate a call: {}".format(json.dumps(call, cls=PiphoneJSONEncoder))) logging.debug("Originate a call: {}".format(json.dumps(call, cls=PiphoneJSONEncoder)))
head = {'call': call.url() head = {'call': call.url()
, 'user': request.params['api'] , 'user': request.params['api']
, 'hits': 1} , 'hits': 1}
return {'header': head, 'data': call} return {'header': head, 'data': call}
except Exception as e:
logging.debug("Missing params : {}".format([p for p in request.params],))
logging.debug("Exception catched: {}".format(e,))
abort(400, "Missing or incorrect fields, the call cannot be processed")
if __name__ == '__main__': if __name__ == '__main__':
app.install(SQLitePlugin(dbfile='call.db')) app.install(SQLitePlugin(dbfile='call.db'))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment