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 call_caller(self): def change(self, event):
'''
Let's change the state of the call
'''
self.update((event['channel']['state'], event['timestamp'],))
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.
''' '''
self.db.execute('''INSERT OR REPLACE INTO calls (caller, callee, owner, callid, history) logging.debug("Saving call {}: {}".format(self.id, json.dumps(self, cls=PiphoneJSONEncoder)))
VALUES (?, ?, ?, ?, ?) try:
''', (self.caller, self.callee, self.owner, self.id, json.dumps(self.history))) self.db.execute('''INSERT OR REPLACE INTO calls (caller, callee, owner, callid, history)
VALUES (?, ?, ?, ?, ?) '''
, (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))
results = db.execute('SELECT callid FROM calls WHERE owner = ?;', (request.params['api'],)) if callid == None:
calls = [] try:
for call in results.fetchall(): results = db.execute('SELECT callid FROM calls WHERE owner = ?;', (request.params['api'],))
calls.append(Call.load(call[0], db)) calls = []
head = {'call': request.fullpath, 'user': request.params['api'], 'hits': len(calls)} for row in results.fetchall():
return {'head': head, 'data': calls} call = Call.load(row[0], db)
logging.debug("Call fetched: {}".format(json.dumps(call, cls=PiphoneJSONEncoder)))
@app.post('/calls/<callid>/caller/<caller>/callee/<callee>/') calls.append(call)
head = {'call': request.fullpath, 'user': request.params['api'], 'hits': len(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/')
@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))
logging.debug("Originate a call: {}".format(json.dumps(call, cls=PiphoneJSONEncoder))) try:
head = {'call': call.url() 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)))
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