From 9770b2b7290d44c58cb66b7e7a83a7940fe8aedd Mon Sep 17 00:00:00 2001 From: Mallika Miglani Date: Tue, 5 Dec 2023 16:20:26 -0500 Subject: [PATCH] init code --- app/auth.py | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ app/messages.py | 66 +++++++++++++++++++++++++++++++++++++ app/server.py | 51 +++++++++++++++++++++++++++++ run_server.py | 5 +++ 4 files changed, 209 insertions(+) create mode 100644 app/auth.py create mode 100644 app/messages.py create mode 100644 app/server.py create mode 100644 run_server.py diff --git a/app/auth.py b/app/auth.py new file mode 100644 index 0000000..bc89538 --- /dev/null +++ b/app/auth.py @@ -0,0 +1,87 @@ +# app/auth.py +"""Handles all auth related APIs.""" + +import datetime +import json +import requests + +class AuthManager: + """Authorization Manager.""" + + LOGIN_URL = "https://api.staging.tides.coloredcow.com/api/v1/session" + RENEW_URL = "https://api.staging.tides.coloredcow.com/api/v1/session/renew" + + def __init__(self): + self.access_token = None + self.renewal_token = None + self.expiry_time = None + + def login(self): + """Create a new session for an existing user.""" + + payload = json.dumps({ + "user": { + "phone": "917834811114", + "password": "secret1234" + } + }) + + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + + response = requests.request("POST", self.LOGIN_URL, headers=headers, data=payload) + + print(response.text) + + if response.status_code == 200: + data = response.json() + self.access_token = data.get('access_token') + self.renewal_token = data.get('renewal_token') + self.expiry_time = (datetime.datetime.now() + + datetime.timedelta(seconds=data.get('token_expiry_time'))) + return True + return False + + def renew_token(self): + """Renew an existing session.""" + + payload = json.dumps({ + "data": { + "data": { + "access_token": self.access_token, + "renewal_token": self.renewal_token, + } + } + }) + + # TODO: Confirm if this = 'Authorization key which includes the renew token.' + # Seems like this is expected according to api.docs/includes/_auth.md line 120. + + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': self.access_token, + } + + response = requests.request("POST", self.RENEW_URL, headers=headers, data=payload) + + print(response.text) + + # TODO: Found format of respnse returned in api.docs/includes/_auth.md, + # glific api docs (https://api.glific.com/#23b77640-b818-459b-88e0-3437665bf7ad) + # say no response returned + + if response.status_code == 200: + data = response.json() + self.access_token = data.get('access_token') + self.renewal_token = data.get('renewal_token') + self.expiry_time = datetime.datetime.now() + datetime.timedelta( + seconds=data.get('token_expiry_time')) + return True + return False + + def is_token_expired(self): + """Check if the token has expired""" + return datetime.datetime.now() >= self.expiry_time diff --git a/app/messages.py b/app/messages.py new file mode 100644 index 0000000..0d51c0c --- /dev/null +++ b/app/messages.py @@ -0,0 +1,66 @@ +# app/glific_api.py +"""Handles message related API requests (Create + Send for now).""" + +import json +import requests + +class MsgManager: + """Message Manager.""" + + SEND_MSG_URL = "https://api.staging.tides.coloredcow.com/api" + + def __init__(self, auth_manager): + self.auth_manager = auth_manager + + def send_message(self, contact_id, message): + """Create + Send a Message.""" + payload= json.dumps({ + "query": """ + mutation createAndSendMessage($input: MessageInput!) { + createAndSendMessage(input: $input) { + message { + id + body + receiver { + id + name + } + } + errors { + key + message + } + } + } + """, + "variables": { + "input": { + "body": message.body, + "flow": "OUTBOUND", + # "isHSM": False, + # "mediaId": 2, + # "params": [], + # "templateId": 4, + # "type": "TEXT", + # TODO: What should values here be? + "senderId": contact_id, + "receiverId": contact_id, + } + } + }) + + + # TODO: Confirm this access token == auth token used in api description. + headers = { + 'authorization': self.auth_manager.access_token, + 'Content-Type': 'application/json' + } + + response = requests.request("POST", self.SEND_MSG_URL, headers=headers, data=payload) + + print(response.text) + + # TODO: Confirm no data members need to be modified when message sent + if response.status_code == 200: + return True + return False diff --git a/app/server.py b/app/server.py new file mode 100644 index 0000000..090d2fb --- /dev/null +++ b/app/server.py @@ -0,0 +1,51 @@ +# app/server.py +""" Long-running server, implements auth + send message functionalities.""" + +from flask import Flask, request, jsonify +from apscheduler.schedulers.background import BackgroundScheduler +from app.auth import AuthManager +from app.messages import MsgManager + +app = Flask(__name__) +auth_manager = AuthManager() +msg_manager = MsgManager(auth_manager) + +def check_and_refresh_token(): + """Attempts to refresh token if expired.""" + + if auth_manager.is_token_expired() and not auth_manager.renew_token(): + # TODO: Potential error message - Confirm + return jsonify({"error":{"message":"Failed to refresh token","status":401}}) + + return jsonify() + +scheduler = BackgroundScheduler() +scheduler.add_job(func=check_and_refresh_token, trigger="interval", minutes=5) +scheduler.start() + +# TODO: Confirm what the URL endpoint here should be - test with glific frontend. +@app.route('/login', methods=['POST']) +def login(): + """Create a new session for an existing user.""" + + if auth_manager.login(): + return jsonify({'message': 'Login successful', 'access_token': auth_manager.access_token}) + + return jsonify({"error":{"message":"Invalid phone or password","status":401}}) + +@app.route('/send_message/', methods=['POST']) +def send_message(contact_id): + """Creates and sends message.""" + + check_and_refresh_token() + + message = request.json.get('message') + + # TODO: Dummy return values for now + if msg_manager.send_message(contact_id, message): + return jsonify({'message': 'Send successful', "status":200}) + + return jsonify({"error":{"message":"Unable to send message","status":401}}) + +if __name__ == '__main__': + app.run(debug=True) diff --git a/run_server.py b/run_server.py new file mode 100644 index 0000000..2f76f05 --- /dev/null +++ b/run_server.py @@ -0,0 +1,5 @@ +# run_server.py +from app.server import app + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file