(20241129) Started talking to MariaDB for integration.

This commit is contained in:
2024-11-29 12:58:39 +05:30
parent 4ed028e486
commit aab700b3ed
11 changed files with 902 additions and 33 deletions
+7 -4
View File
@@ -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()
+4 -5
View File
@@ -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,
View File
+202
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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",