(20241129) Started talking to MariaDB for integration.
This commit is contained in:
@@ -163,8 +163,11 @@ async def mail_callback(
|
||||
tokens.email = user_profile.data["emailAddress"] if user_profile.success else None
|
||||
|
||||
# Save the tokens to the database
|
||||
tokens_saved = await MailOAuthModel.set_token(
|
||||
db_conn = current_app.data_mongo,
|
||||
tokens_saved = await current_app.mail_oauth_model.set_token(
|
||||
db_conn = current_app.sql_writer,
|
||||
mongo_conn = current_app.data_mongo,
|
||||
session_token = inbound_headers["X-Session-Token"],
|
||||
user_info = kwargs["session_info"],
|
||||
user_identifier = inbound_data["state"],
|
||||
token = tokens.model_dump()
|
||||
)
|
||||
@@ -174,7 +177,7 @@ async def mail_callback(
|
||||
# ┛┗┗ ┛┣┛┗┛┛┗┛┗
|
||||
# ┛
|
||||
|
||||
# # Return a response:
|
||||
# # Return a JSON response:
|
||||
# return ResponseModel(
|
||||
# status_code = StatusCodes.OK if tokens_saved else StatusCodes.FAILED,
|
||||
# http_code = HttpCodes.SUCCESS if tokens_saved else HttpCodes.INTERNAL_SERVER_ERROR,
|
||||
@@ -184,7 +187,7 @@ async def mail_callback(
|
||||
# }
|
||||
# )
|
||||
|
||||
# Return a page that shows you the status of your authorization:
|
||||
# Return an HTML response:
|
||||
return await render_template(
|
||||
"/mail/oauth/oauth_success.html" if tokens_saved else "/mail/oauth/oauth_failure.html",
|
||||
mail_client = mail_client.title()
|
||||
|
||||
@@ -66,9 +66,6 @@ from utils_v2.goog.gmail.gmail_client import SCOPES_GMAIL_MAIL_MANAGEMENT
|
||||
# Common:
|
||||
from shared import constants
|
||||
|
||||
# Behaviour Models:
|
||||
from models.behaviour.mail.oauth import MailOAuthModel
|
||||
|
||||
# Data Models:
|
||||
from models.data.mail.oauth import (
|
||||
OAuthMailAuthorizationRequestHeaders,
|
||||
@@ -178,8 +175,10 @@ async def request_oauth_authorization_url(
|
||||
# ┛
|
||||
|
||||
# Make a user identifier from the session info:
|
||||
user_identifier = await MailOAuthModel.get_id(
|
||||
db_conn = current_app.data_mongo,
|
||||
user_identifier = await current_app.mail_oauth_model.get_user_identifier(
|
||||
db_conn = current_app.sql_writer,
|
||||
mongo_conn = current_app.data_mongo,
|
||||
session_token = inbound_headers["X-Session-Token"],
|
||||
user_info = kwargs["session_info"],
|
||||
service_type = "email",
|
||||
service_client = inbound_data.mailClient,
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Friday, 29th Nov., 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To manage alert sending from one place. You may have various outlets for alerts, like Telegram, WhatsApp, etc.
|
||||
Add endpoints for them here. Note that these alerts will be for the internal tech admins. It won't be for the
|
||||
client-facing comms. Create a separate blueprint for that.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
N/A
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
NOTES:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# To make sibling directories accessible for imports:
|
||||
import sys
|
||||
sys.path.append(".")
|
||||
sys.path.append("..")
|
||||
|
||||
# For using Quart:
|
||||
from quart import Blueprint, current_app
|
||||
|
||||
# My utils:
|
||||
from utils_v2.string import json
|
||||
from utils_v2.date_time import date_time
|
||||
from utils_v2.api.codes import StatusCodes, HttpCodes
|
||||
from utils_v2.api.response import ResponseModel
|
||||
from utils_v2.api.async_quart import (
|
||||
set_api_version,
|
||||
read_input,
|
||||
get_session_info,
|
||||
log_request_to_mongo,
|
||||
log_chain_to_mongo,
|
||||
should_not_be_under_maintenance,
|
||||
only_whitelisted_ips,
|
||||
limit_rate,
|
||||
validate_input,
|
||||
handle_cancelled_request
|
||||
)
|
||||
|
||||
# Data models:
|
||||
from models.data.tech.alerts import (
|
||||
ChatAlertRequestHeaders,
|
||||
ChatAlertRequestData
|
||||
)
|
||||
|
||||
# Common:
|
||||
from shared import constants
|
||||
|
||||
# For asynchronous activities:
|
||||
import asyncio
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# Related to Quart:
|
||||
tech_chat_alert_bp = Blueprint("tech_chat_alert", __name__)
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
@tech_chat_alert_bp.record_once
|
||||
def init(blueprint_setup_state):
|
||||
|
||||
# This gets called when the blueprint is registered.
|
||||
# Consider this to be a one-time setup for the whole blueprint:
|
||||
pass
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@tech_chat_alert_bp.route("/chat/<source>", methods = ["POST"])
|
||||
@set_api_version(api_version = "3.0.0")
|
||||
@read_input(sanitize_headers = True, sanitize_data = True)
|
||||
@get_session_info(key = "X-Session-Token", session_coro = "get_session")
|
||||
@should_not_be_under_maintenance(attr_name = "is_under_maintenance")
|
||||
@validate_input(
|
||||
header_validator = lambda x: ChatAlertRequestHeaders(**x).model_dump(),
|
||||
data_validator = lambda x: ChatAlertRequestData(**x)
|
||||
)
|
||||
@handle_cancelled_request()
|
||||
async def user_login(
|
||||
source: str = None,
|
||||
inbound_headers: dict = None,
|
||||
inbound_data: dict | ChatAlertRequestData = None,
|
||||
inbound_files: dict = None,
|
||||
**kwargs
|
||||
):
|
||||
|
||||
"""
|
||||
To relay an alert over some form of chat client like Telegram and WhatsApp.
|
||||
:param source: The identifier of the source of the alert.
|
||||
:param inbound_headers: auto-extracted by the decorators.
|
||||
:param inbound_data: auto-extracted by the decorators.
|
||||
:param inbound_files: auto-extracted by the decorators.
|
||||
:param kwargs: Any number of extra inputs supplied by the decorators.
|
||||
:return: A standard response structure.
|
||||
"""
|
||||
|
||||
response = None
|
||||
|
||||
# ┳┳┓ ┏┓ •
|
||||
# ┃┃┃┏┓┏┏┏┓┏┓┏┓ ┃ ┏┓┏┓┏╋┏┓┓┏┏╋┓┏┓┏┓
|
||||
# ┛ ┗┗ ┛┛┗┻┗┫┗ ┗┛┗┛┛┗┛┗┛ ┗┻┗┗┗┗┛┛┗
|
||||
# ┛
|
||||
|
||||
# Capture the event's time, and the person who caused the event:
|
||||
event_dt = date_time.get_current_ist_date_time().strftime("%Y-%m-%d %I:%M:%S %p %Z")
|
||||
if kwargs.get("session_info"): username = kwargs["session_info"].get("fullName")
|
||||
else: username = None
|
||||
|
||||
# Construct the prefix:
|
||||
if inbound_data.type == "warning": message_type = "⚠️ WARNING FROM"
|
||||
elif inbound_data.type == "error": message_type = "🚨 ERROR IN"
|
||||
else: message_type = "ℹ️ INFO FROM"
|
||||
message_prefix = f"{message_type} *TCAOFF (Converse)*!\n{event_dt}\n\n"
|
||||
message_prefix += f"*From:*\n`{username} (via '{source}')`\n\n"
|
||||
|
||||
# ┏┳┓ ┓
|
||||
# ┃ ┏┓┃┏┓┏┓┏┓┏┓┏┳┓
|
||||
# ┻ ┗ ┗┗ ┗┫┛ ┗┻┛┗┗
|
||||
# ┛
|
||||
|
||||
if inbound_data.chatClient == "telegram":
|
||||
response = await current_app.http_client.post(
|
||||
url = current_app.script_data["telegram"]["connectors"]["nexcom"]["url"],
|
||||
json = {
|
||||
"appKey": current_app.script_data["telegram"]["connectors"]["nexcom"]["appKey"],
|
||||
"chatId": inbound_data.chatId or current_app.script_data["telegram"]["chatIds"]["tcaoff"],
|
||||
"message": message_prefix + inbound_data.message
|
||||
}
|
||||
)
|
||||
|
||||
# ┳┓
|
||||
# ┣┫┏┓┏┏┓┏┓┏┓┏┏┓
|
||||
# ┛┗┗ ┛┣┛┗┛┛┗┛┗
|
||||
# ┛
|
||||
|
||||
# Construct and return the response:
|
||||
response_json = response.json()
|
||||
success = True if response_json.get("status", 0) == 1 else False
|
||||
return ResponseModel(
|
||||
status_code = StatusCodes.OK if success else StatusCodes.FAILED,
|
||||
message = response_json.get("message", "unknown failure")
|
||||
)
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pass
|
||||
@@ -87,6 +87,7 @@ async def get_session(session_token):
|
||||
raw_info = await current_app.module_cache.get(key = session_token)
|
||||
session_info = {
|
||||
"fullName": raw_info["value"]["full_name"],
|
||||
"userId": raw_info["value"]["user_id"],
|
||||
"entityId": raw_info["value"]["entity_id"],
|
||||
"billingAccountId": raw_info["value"]["billing_account_id"],
|
||||
"departmentId": raw_info["value"]["department_id"],
|
||||
|
||||
+64
-1
@@ -53,6 +53,7 @@ from utils_v2.string import json
|
||||
from utils_v2.api import async_quart
|
||||
from utils_v2.date_time import date_time
|
||||
from utils_v2.database.async_mongo_v2 import AsyncMongo
|
||||
from utils_v2.database.async_mysql_v2 import AsyncMySQL
|
||||
from utils_v2.cache.async_redis_cache_v2 import AsyncRedisCache
|
||||
from utils_v2.serialization.json_serializer import JSONSerializer
|
||||
from utils_v2.api.async_quart import (
|
||||
@@ -67,7 +68,10 @@ from utils_v2.api.async_quart import (
|
||||
)
|
||||
|
||||
# GMail-related utils:
|
||||
from utils_v2.goog.gmail.gmail_client import AsyncGMailClient, SCOPES_GMAIL_MAIL_MANAGEMENT
|
||||
from utils_v2.goog.gmail.gmail_client import AsyncGMailClient
|
||||
|
||||
# Behaviour Models:
|
||||
from models.behaviour.mail.oauth import MailOAuthModel
|
||||
|
||||
# To make REST API calls:
|
||||
import httpx
|
||||
@@ -78,6 +82,7 @@ from icecream import IceCreamDebugger
|
||||
# All the blueprints:
|
||||
from api.blueprints.mail.oauth_request import mail_oauth_bp
|
||||
from api.blueprints.mail.oauth_callback import mail_callback_bp
|
||||
from api.blueprints.tech.chat_alerts import tech_chat_alert_bp
|
||||
from api.blueprints.test.callback import test_callback_bp
|
||||
|
||||
# All the helpers:
|
||||
@@ -108,6 +113,7 @@ app = Quart(__name__, template_folder = r"../views")
|
||||
app = cors(app)
|
||||
app.register_blueprint(mail_oauth_bp, url_prefix = f"/{MODULE_BASE}/mail")
|
||||
app.register_blueprint(mail_callback_bp, url_prefix = f"/{MODULE_BASE}/mail")
|
||||
app.register_blueprint(tech_chat_alert_bp, url_prefix = f"/{MODULE_BASE}/tech/alert")
|
||||
app.register_blueprint(test_callback_bp, url_prefix = f"/{MODULE_BASE}/test")
|
||||
|
||||
|
||||
@@ -184,6 +190,10 @@ async def app_startup(**kwargs):
|
||||
)
|
||||
)
|
||||
|
||||
# ┏┓ ┓ ┓ ┳┓
|
||||
# ┃ ┏┓┏┓┏┫ ┏┓┏┓┏┫ ┃┃┏┓╋┏┓
|
||||
# ┗┛┛ ┗ ┗┻ ┗┻┛┗┗┻ ┻┛┗┻┗┗┻
|
||||
|
||||
# Get the script credentials and data:
|
||||
script_id = os.environ["SCRIPT_ID"]
|
||||
response = await current_app.http_client.get(
|
||||
@@ -197,10 +207,19 @@ async def app_startup(**kwargs):
|
||||
)
|
||||
current_app.script_data = response.json().get("data")
|
||||
|
||||
# ┏┓┏┓┳┳ ┏┓┏┏• •
|
||||
# ┃ ┃┃┃┃ ┣┫╋╋┓┏┓┓╋┓┏
|
||||
# ┗┛┣┛┗┛ ┛┗┛┛┗┛┗┗┗┗┫
|
||||
# ┛
|
||||
|
||||
# We set the CPU affinity:
|
||||
try: set_cpu_affinity(script_cred["cpuAffinity"])
|
||||
except Exception as exception: current_app.printer(exception)
|
||||
|
||||
# ┳┓ ┓• ┏┓ ┓
|
||||
# ┣┫┏┓┏┫┓┏ ━━ ┃ ┏┓┏┣┓┏┓
|
||||
# ┛┗┗ ┗┻┗┛ ┗┛┗┻┗┛┗┗
|
||||
|
||||
# Caching connections:
|
||||
current_app.rate_limit_cache = AsyncRedisCache(
|
||||
connection_string = script_cred["redisCache"]["rateLimit"]["connectionString"],
|
||||
@@ -214,6 +233,33 @@ async def app_startup(**kwargs):
|
||||
debug_prefix = "User Cache | "
|
||||
)
|
||||
|
||||
# ┳┳┓ • ┳┓┳┓
|
||||
# ┃┃┃┏┓┏┓┓┏┓┃┃┣┫
|
||||
# ┛ ┗┗┻┛ ┗┗┻┻┛┻┛
|
||||
|
||||
# MariaDB connections:
|
||||
current_app.sql_writer = AsyncMySQL(
|
||||
pool_size = script_cred["mariaDb"]["write"]["poolSize"],
|
||||
host = script_cred["mariaDb"]["write"]["host"],
|
||||
user = script_cred["mariaDb"]["write"]["user"],
|
||||
password = script_cred["mariaDb"]["write"]["password"],
|
||||
database = script_cred["mariaDb"]["write"]["database"]
|
||||
)
|
||||
await current_app.sql_writer.connect()
|
||||
current_app.sql_reader = AsyncMySQL(
|
||||
pool_size = script_cred["mariaDb"]["read"]["poolSize"],
|
||||
host = script_cred["mariaDb"]["read"]["host"],
|
||||
user = script_cred["mariaDb"]["read"]["user"],
|
||||
password = script_cred["mariaDb"]["read"]["password"],
|
||||
database = script_cred["mariaDb"]["read"]["database"]
|
||||
)
|
||||
await current_app.sql_reader.connect()
|
||||
|
||||
# ┳┳┓
|
||||
# ┃┃┃┏┓┏┓┏┓┏┓
|
||||
# ┛ ┗┗┛┛┗┗┫┗┛
|
||||
# ┛
|
||||
|
||||
# MongoDB connections:
|
||||
current_app.logs_mongo = AsyncMongo(
|
||||
connection_string = script_cred["mongoDb"]["logs"]["connectionString"],
|
||||
@@ -230,6 +276,23 @@ async def app_startup(**kwargs):
|
||||
)
|
||||
await current_app.data_mongo.connect()
|
||||
|
||||
# ┳ ┓ ┳┳┓ ┓ ┓
|
||||
# ┃┏┓╋┏┓┏┓┏┓┏┓┃ ┃┃┃┏┓┏┫┏┓┃┏
|
||||
# ┻┛┗┗┗ ┛ ┛┗┗┻┗ ┛ ┗┗┛┗┻┗ ┗┛
|
||||
|
||||
current_app.mail_oauth_model = MailOAuthModel(
|
||||
cache = current_app.module_cache,
|
||||
alert_url = current_app.script_data["alerts"]["url"],
|
||||
http_client = current_app.http_client,
|
||||
debug = enable_debugging,
|
||||
debug_prefix = "Mail-OAuth | ",
|
||||
debug_only_errors = True
|
||||
)
|
||||
|
||||
# ┏┓
|
||||
# ┃ ┏┓┏┓┏┓┏┓┏╋┏┓┏┓┏
|
||||
# ┗┛┗┛┛┗┛┗┗ ┗┗┗┛┛ ┛
|
||||
|
||||
# Create an instance to handle GMail-related activities:
|
||||
current_app.gmail_client = AsyncGMailClient(
|
||||
service_name = "gmail",
|
||||
|
||||
Reference in New Issue
Block a user