(20241205) Migrating from the field "accountId" to "tokenId" to identify auth tokens.

This commit is contained in:
2024-12-05 11:19:23 +05:30
parent f835143ef4
commit 5c36f90e98
15 changed files with 478 additions and 47 deletions
+2 -2
View File
@@ -165,9 +165,9 @@ async def list_mails_for_account_id(
) )
# Get the mail: # Get the mail:
mails_list = await current_app.mail_retrieve_model.list_for_account_identifier( mails_list = await current_app.mail_retrieve_model.list_for_token_id(
mongo_conn = current_app.data_mongo, mongo_conn = current_app.data_mongo,
account_identifier = inbound_data.accountId, token_id = inbound_data.tokenId,
limit = inbound_data.count, limit = inbound_data.count,
skip = inbound_data.fromCount skip = inbound_data.fromCount
) )
+2 -2
View File
@@ -159,7 +159,7 @@ async def handle_gmail_callback() -> render_template:
# if they don't match, we reject the authorization: # if they don't match, we reject the authorization:
placeholder_token = await current_app.mail_oauth_model.get_token( placeholder_token = await current_app.mail_oauth_model.get_token(
mongo_conn = current_app.data_mongo, mongo_conn = current_app.data_mongo,
account_identifier = g.inbound_data["state"] token_id = g.inbound_data["state"]
) )
if ( if (
(not placeholder_token) or (not placeholder_token) or
@@ -210,7 +210,7 @@ async def handle_gmail_callback() -> render_template:
db_conn = current_app.sql_writer, db_conn = current_app.sql_writer,
mongo_conn = current_app.data_mongo, mongo_conn = current_app.data_mongo,
session_token = g.inbound_headers.get("X-Session-Token"), session_token = g.inbound_headers.get("X-Session-Token"),
account_identifier = g.inbound_data["state"], token_id = g.inbound_data["state"],
email_id = tokens.email, email_id = tokens.email,
token = tokens.model_dump() token = tokens.model_dump()
) )
+5 -5
View File
@@ -175,7 +175,7 @@ async def request_oauth_authorization_url(
# ┛ # ┛
# Make a user identifier from the session info: # Make a user identifier from the session info:
user_identifier = await current_app.mail_oauth_model.get_account_identifier( token_id = await current_app.mail_oauth_model.get_token_id(
db_conn = current_app.sql_writer, db_conn = current_app.sql_writer,
mongo_conn = current_app.data_mongo, mongo_conn = current_app.data_mongo,
session_token = inbound_headers["X-Session-Token"], session_token = inbound_headers["X-Session-Token"],
@@ -184,13 +184,13 @@ async def request_oauth_authorization_url(
service_client = inbound_data.mailClient, service_client = inbound_data.mailClient,
auth_type = "oauth" auth_type = "oauth"
) )
if user_identifier is None: if token_id is None:
return ResponseModel( return ResponseModel(
status_code = StatusCodes.FAILED, status_code = StatusCodes.FAILED,
http_code = HttpCodes.INTERNAL_SERVER_ERROR, http_code = HttpCodes.INTERNAL_SERVER_ERROR,
message = "failed to generate user identifier" message = "failed to generate token id"
) )
user_identifier = str(user_identifier) token_id = str(token_id)
# ┏┓ ┏┓┳┳┓ •┓ # ┏┓ ┏┓┳┳┓ •┓
# ┣ ┏┓┏┓ ┃┓┃┃┃┏┓┓┃ # ┣ ┏┓┏┓ ┃┓┃┃┃┏┓┓┃
@@ -201,7 +201,7 @@ async def request_oauth_authorization_url(
# Get the authorization URL: # Get the authorization URL:
auth_url = await current_app.gmail_client.get_authorization_url( auth_url = await current_app.gmail_client.get_authorization_url(
scopes = SCOPES_GMAIL_MAIL_MANAGEMENT, scopes = SCOPES_GMAIL_MAIL_MANAGEMENT,
state = user_identifier, state = token_id,
access_type = "offline", access_type = "offline",
approval_prompt = "force", approval_prompt = "force",
include_granted_scopes = "true", include_granted_scopes = "true",
+1 -1
View File
@@ -166,7 +166,7 @@ async def get_one_mail(
# Get the mail: # Get the mail:
mail_data = await current_app.mail_retrieve_model.get_mail( mail_data = await current_app.mail_retrieve_model.get_mail(
mongo_conn = current_app.data_mongo, mongo_conn = current_app.data_mongo,
mail_identifier = inbound_data.mailId mail_id = inbound_data.mailId
) )
# Done here: # Done here:
+1 -1
View File
@@ -146,7 +146,7 @@ async def sync_mails(
return await current_app.mail_sync_model.sync( return await current_app.mail_sync_model.sync(
session_token = inbound_headers["X-Session-Token"], session_token = inbound_headers["X-Session-Token"],
mongo_conn = mongo_conn, mongo_conn = mongo_conn,
account_identifier = inbound_data.accountId, token_id = inbound_data.tokenId,
llm = llm, llm = llm,
force_sync = inbound_data.forceSync, force_sync = inbound_data.forceSync,
start_date = inbound_data.startDate, start_date = inbound_data.startDate,
View File
+236
View File
@@ -0,0 +1,236 @@
"""
AUTHOR:
Khushal P Soonderji
DATE:
Thursday, 5th Dec., 2024
OBJECTIVE:
To receive auth details for various SMS client APIs.
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, request
# My utils:
from utils_v2.string import json
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
)
# SMS-related utils:
from utils_v2.sms.nimbus.async_nimbus import AsyncNimbusSMS
from utils_v2.sms.savvy_bulk_sms.async_savvy_bulk_sms import AsyncSavvyBulkSMS
# Common:
from shared import constants
# Data Models:
from models.data.mail.oauth import (
OAuthMailAuthorizationRequestHeaders,
OAuthMailAuthorizationRequestData
)
# For asynchronous activities:
import asyncio
# *****************************************************************************************************************
# ***** ****
# *** MACROS / ONE-TIME INIT ***
# ***** ****
# *****************************************************************************************************************
# Related to Quart:
sms_auth_bp = Blueprint("sms_auth", __name__)
# *****************************************************************************************************************
# ***** ****
# *** VARIABLES ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** FUNCTIONS ***
# ***** ****
# *****************************************************************************************************************
@sms_auth_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
# ---------------------------------------------------------------------------------------------------------------------
@sms_auth_bp.route("/auth", methods = ["GET"])
@set_api_version(api_version = "1.0.0")
@read_input(sanitize_headers = False, sanitize_data = False)
@get_session_info(key = "X-Session-Token", session_coro = "get_session")
@log_request_to_mongo(
attr_name = "logs_mongo",
project = constants.PROJECT_NAME,
log_type = constants.MODULE_NAME,
operation = "smsAuthApi",
log_input = True,
log_output = True,
sensitive_keys = ["sessionToken", "X-Session-Token"]
)
@log_chain_to_mongo(attr_name = "logs_mongo")
@should_not_be_under_maintenance(attr_name = "is_under_maintenance")
@validate_input(
header_validator = lambda x: OAuthMailAuthorizationRequestHeaders(**x).model_dump(),
data_validator = lambda x: OAuthMailAuthorizationRequestData(**x)
)
@handle_cancelled_request()
async def request_oauth_authorization_url(
inbound_headers: dict | OAuthMailAuthorizationRequestHeaders = None,
inbound_data: dict | OAuthMailAuthorizationRequestData = None,
inbound_files: dict = None,
**kwargs
):
"""
Use this when requesting access to someone's GMail account. This API should be used from the UI. A button click
"Connect to GMail" should hit this API, which will generate a request to gain access to the user's GMail account.
When the URL is hit, it opens Google's own UI, and, when the user clicks "Continue", Google hits your 'redirect_url'
to inform you about the user's action.
: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.
"""
# ┏┓
# ┃┃┏┓┏┓┏┓┏┓┏┓┏┏┓┏┏
# ┣┛┛ ┗ ┣┛┛ ┗┛┗┗ ┛┛
# ┛
# If the session token is invalid/expired:
if kwargs.get("session_info") is None:
return ResponseModel(
status_code = StatusCodes.FAILED,
http_code = HttpCodes.UNAUTHORIZED
)
# Start by assuming failure:
auth_url = None
# ┳ ┓ •┏ ┳┳
# ┃┏┫┏┓┏┓╋┓╋┓┏ ┃┃┏┏┓┏┓
# ┻┗┻┗ ┛┗┗┗┛┗┫ ┗┛┛┗ ┛
# ┛
# Make a user identifier from the session info:
user_identifier = await current_app.mail_oauth_model.get_account_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"],
email_id = inbound_data.mailId,
service_client = inbound_data.mailClient,
auth_type = "oauth"
)
if user_identifier is None:
return ResponseModel(
status_code = StatusCodes.FAILED,
http_code = HttpCodes.INTERNAL_SERVER_ERROR,
message = "failed to generate user identifier"
)
user_identifier = str(user_identifier)
# ┏┓ ┏┓┳┳┓ •┓
# ┣ ┏┓┏┓ ┃┓┃┃┃┏┓┓┃
# ┻ ┗┛┛ ┗┛┛ ┗┗┻┗┗
if inbound_data.mailClient == "gmail":
# Get the authorization URL:
auth_url = await current_app.gmail_client.get_authorization_url(
scopes = SCOPES_GMAIL_MAIL_MANAGEMENT,
state = user_identifier,
access_type = "offline",
approval_prompt = "force",
include_granted_scopes = "true",
user_email = inbound_data.mailId
)
# ┳┓
# ┣┫┏┓┏┏┓┏┓┏┓┏┏┓
# ┛┗┗ ┛┣┛┗┛┛┗┛┗
# ┛
# Done here:
return ResponseModel(
status_code = StatusCodes.OK if auth_url else StatusCodes.FAILED,
http_code = HttpCodes.SUCCESS if auth_url else HttpCodes.INTERNAL_SERVER_ERROR,
data = {
"mailClient": inbound_data.mailClient,
"authorizationUrl": auth_url
}
)
# *****************************************************************************************************************
# ***** ****
# *** MAIN PROGRAM ***
# ***** ****
# *****************************************************************************************************************
if __name__ == "__main__":
pass
+20 -11
View File
@@ -95,7 +95,7 @@ class MailOAuthModel(BaseModel):
AUTH_COLLECTION = "_authTokens" AUTH_COLLECTION = "_authTokens"
async def get_account_identifier( async def get_token_id(
self, self,
db_conn: AsyncMySQL, db_conn: AsyncMySQL,
mongo_conn: AsyncMongo, mongo_conn: AsyncMongo,
@@ -186,7 +186,7 @@ class MailOAuthModel(BaseModel):
self, self,
db_conn: AsyncMySQL, db_conn: AsyncMySQL,
mongo_conn: AsyncMongo, mongo_conn: AsyncMongo,
account_identifier: ObjectId | str, token_id: ObjectId | str,
email_id: str, email_id: str,
token: dict, token: dict,
session_token: str = None session_token: str = None
@@ -198,9 +198,9 @@ class MailOAuthModel(BaseModel):
ALSO. ALSO.
:param db_conn: The database connection (MariaDB) to use to perform the action. :param db_conn: The database connection (MariaDB) to use to perform the action.
:param mongo_conn: The database connection (MongoDB) to use to perform the action. :param mongo_conn: The database connection (MongoDB) to use to perform the action.
:param account_identifier: The identifier granted by the 'get_account_identifier' method. :param token_id: The identifier granted by the 'get_token_id' method.
:param email_id: The e-mail id that the user tried to connect to your service. This should match the e-mail id :param email_id: The e-mail id that the user tried to connect to your service. This should match the e-mail id
the user claimed he wants to connect when he used 'get_account_identifier'. the user claimed he wants to connect when he used 'get_token_identifier'.
:param token: The token granted by the third-party service. :param token: The token granted by the third-party service.
:param session_token: The session token of the user who requested this service. :param session_token: The session token of the user who requested this service.
:return: True if saved, False if failed. :return: True if saved, False if failed.
@@ -216,7 +216,7 @@ class MailOAuthModel(BaseModel):
mongo_json = await mongo_conn.find_one_and_update( mongo_json = await mongo_conn.find_one_and_update(
collection = MailOAuthModel.AUTH_COLLECTION, collection = MailOAuthModel.AUTH_COLLECTION,
filter = mongo_conn.dict_to_dot_notation({ filter = mongo_conn.dict_to_dot_notation({
"_id": ObjectId(account_identifier), "_id": ObjectId(token_id),
"clientUserId": { "clientUserId": {
"email": email_id "email": email_id
} }
@@ -225,9 +225,18 @@ class MailOAuthModel(BaseModel):
"$set": { "$set": {
"token": token, "token": token,
"lastRefreshTs": request_ts, "lastRefreshTs": request_ts,
"firstRefreshTs": {
"$cond": {
"if": {
"$or": [
{"$eq": ["$fieldName", None]},
{"$eq": [{"$type": "$fieldName"}, "missing"]}
]
}, },
"$setOnInsert": { "then": request_ts,
"firstRefreshTs": request_ts, "else": "$firstRefreshTs"
}
}
} }
}, },
projection = {"token": False}, projection = {"token": False},
@@ -252,7 +261,7 @@ class MailOAuthModel(BaseModel):
"Set Token", # .................................................. 'p_last_action' "Set Token", # .................................................. 'p_last_action'
token["displayName"], # ......................................... 'p_display_name' token["displayName"], # ......................................... 'p_display_name'
token["displayPictureUrl"], # ................................... 'p_display_picture' token["displayPictureUrl"], # ................................... 'p_display_picture'
account_identifier, # ........................................... 'p_token_id' token_id, # ..................................................... 'p_token_id'
json.to_string(python_data = token_notes, no_space = True), # ... 'p_notes' json.to_string(python_data = token_notes, no_space = True), # ... 'p_notes'
mongo_json["user"]["userId"] # .................................. 'p_created_by' mongo_json["user"]["userId"] # .................................. 'p_created_by'
), ),
@@ -266,14 +275,14 @@ class MailOAuthModel(BaseModel):
async def get_token( async def get_token(
self, self,
mongo_conn: AsyncMongo, mongo_conn: AsyncMongo,
account_identifier: ObjectId | str = None, token_id: ObjectId | str = None,
**kwargs **kwargs
) -> dict | None: ) -> dict | None:
""" """
To retrieve stored tokens from the database. To retrieve stored tokens from the database.
:param mongo_conn: The database connection (MongoDB) to use to perform the action. :param mongo_conn: The database connection (MongoDB) to use to perform the action.
:param account_identifier: The identifier granted by the 'account_identifier' method. :param token_id: The identifier granted by the 'get_token_id' method.
:param kwargs: Any set of key-value pairs to build custom search criteria. This could be things like the user :param kwargs: Any set of key-value pairs to build custom search criteria. This could be things like the user
info, the client, the type of authentication used, or even the kind of service. info, the client, the type of authentication used, or even the kind of service.
:return: The retrieved record that has the token, and information about the service and client if found, else :return: The retrieved record that has the token, and information about the service and client if found, else
@@ -282,7 +291,7 @@ class MailOAuthModel(BaseModel):
# Build the filter: # Build the filter:
filter_json = {k: v for k, v in kwargs.items()} filter_json = {k: v for k, v in kwargs.items()}
if account_identifier: filter_json["_id"] = ObjectId(account_identifier) if token_id: filter_json["_id"] = ObjectId(token_id)
# If there is no search criteria, we exit with failure: # If there is no search criteria, we exit with failure:
if not filter_json: return None if not filter_json: return None
+7 -7
View File
@@ -98,20 +98,20 @@ class MailRetrieveModel(BaseModel):
async def get_mail( async def get_mail(
self, self,
mongo_conn: AsyncMongo, mongo_conn: AsyncMongo,
mail_identifier: str | ObjectId mail_id: str | ObjectId
): ):
""" """
Retrieves one full mail from the database. Retrieves one full mail from the database.
:param mongo_conn: The instance of the database connector to use to get the mail's data. :param mongo_conn: The instance of the database connector to use to get the mail's data.
:param mail_identifier: The '_id' of the document that holds the mail. :param mail_id: The '_id' of the document that holds the mail.
:return: Either the JSON that describes the mail or None if such a mail does not exist. :return: Either the JSON that describes the mail or None if such a mail does not exist.
""" """
# Get the data from the database: # Get the data from the database:
mail_data = await mongo_conn.find_one( mail_data = await mongo_conn.find_one(
collection = self.MAIL_COLLECTION, collection = self.MAIL_COLLECTION,
filter = {"_id": ObjectId(mail_identifier)}, filter = {"_id": ObjectId(mail_id)},
projection = { projection = {
"_id": True, "_id": True,
"serviceType": True, "serviceType": True,
@@ -141,10 +141,10 @@ class MailRetrieveModel(BaseModel):
# Done here: # Done here:
return mail_data return mail_data
async def list_for_account_identifier( async def list_for_token_id(
self, self,
mongo_conn: AsyncMongo, mongo_conn: AsyncMongo,
account_identifier: str | ObjectId, token_id: str | ObjectId,
limit: int = 25, limit: int = 25,
skip: int = 0 skip: int = 0
): ):
@@ -152,7 +152,7 @@ class MailRetrieveModel(BaseModel):
""" """
To enlist mails for one account. To enlist mails for one account.
:param mongo_conn: The instance of the database connector to use to get the mail's data. :param mongo_conn: The instance of the database connector to use to get the mail's data.
:param account_identifier: The id of the document in the database that holds the tokens to access the account. :param token_id: The id of the document in the database that holds the tokens to access the account.
:param limit: How many records to fetch. :param limit: How many records to fetch.
:param skip: How many initial records to skip. useful for pagination. :param skip: How many initial records to skip. useful for pagination.
:return: Either the JSON that describes the mails or None if something failed. :return: Either the JSON that describes the mails or None if something failed.
@@ -161,7 +161,7 @@ class MailRetrieveModel(BaseModel):
# Get the data from the database: # Get the data from the database:
mails_list = await mongo_conn.find_many( mails_list = await mongo_conn.find_many(
collection = self.MAIL_COLLECTION, collection = self.MAIL_COLLECTION,
filter = {"accountId": ObjectId(account_identifier)}, filter = {"tokenId": ObjectId(token_id)},
projection = { projection = {
"_id": True, "_id": True,
"serviceType": True, "serviceType": True,
+9 -11
View File
@@ -344,7 +344,7 @@ class MailSyncModel(BaseModel):
self, self,
session_token: str, session_token: str,
mongo_conn: AsyncMongo, mongo_conn: AsyncMongo,
account_identifier: ObjectId, token_id: ObjectId,
mail_client: AsyncGMailClient, mail_client: AsyncGMailClient,
tokens: GoogleAuthTokens, tokens: GoogleAuthTokens,
llm: ChatOpenAI = None, llm: ChatOpenAI = None,
@@ -358,7 +358,7 @@ class MailSyncModel(BaseModel):
Sync many mails from GMail in one shot. Sync many mails from GMail in one shot.
:param session_token: The session token of the uer who is trying to upload this file. :param session_token: The session token of the uer who is trying to upload this file.
:param mongo_conn: The instance of the connection to the database to use. :param mongo_conn: The instance of the connection to the database to use.
:param account_identifier: The id of the document in the database that holds the tokens to access the account. :param token_id: The id of the document in the database that holds the tokens to access the account.
Needed only for refreshing the tokens and saving them. Needed only for refreshing the tokens and saving them.
:param mail_client: The instance of the mail client to use to perform the action. :param mail_client: The instance of the mail client to use to perform the action.
:param tokens: The tokens to use to fetch the mails. :param tokens: The tokens to use to fetch the mails.
@@ -383,7 +383,7 @@ class MailSyncModel(BaseModel):
if tokens_refreshed: await current_app.mail_oauth_model.set_token( if tokens_refreshed: await current_app.mail_oauth_model.set_token(
db_conn = current_app.sql_writer, db_conn = current_app.sql_writer,
mongo_conn = mongo_conn, mongo_conn = mongo_conn,
account_identifier = account_identifier, token_id = token_id,
email_id = tokens.email, email_id = tokens.email,
token = tokens, token = tokens,
session_token = session_token session_token = session_token
@@ -439,7 +439,7 @@ class MailSyncModel(BaseModel):
}, },
replacement = { replacement = {
"version": "1.0.0", "version": "1.0.0",
"accountId": ObjectId(account_identifier), "tokenId": ObjectId(token_id),
"serviceType": "email", "serviceType": "email",
"client": "gmail", "client": "gmail",
"payload": result.mailMessage "payload": result.mailMessage
@@ -461,8 +461,6 @@ class MailSyncModel(BaseModel):
message_ids = [v["id"] for v in messages_list.values()], message_ids = [v["id"] for v in messages_list.values()],
add_label_ids = [tokens.labels.get("TCAOFF", {}).get("id")] add_label_ids = [tokens.labels.get("TCAOFF", {}).get("id")]
) )
print(client_response)
print([tokens.labels.get("TCAOFF")])
except Exception as exception: except Exception as exception:
self._printer(exception) self._printer(exception)
@@ -474,7 +472,7 @@ class MailSyncModel(BaseModel):
self, self,
session_token: str, session_token: str,
mongo_conn: AsyncMongo, mongo_conn: AsyncMongo,
account_identifier: ObjectId, token_id: ObjectId,
llm: ChatOpenAI = None, llm: ChatOpenAI = None,
force_sync: bool = False, force_sync: bool = False,
start_date: datetime.datetime = None, start_date: datetime.datetime = None,
@@ -487,7 +485,7 @@ class MailSyncModel(BaseModel):
route the request to the appropriate clients. route the request to the appropriate clients.
:param session_token: The session token of the uer who is trying to upload this file. :param session_token: The session token of the uer who is trying to upload this file.
:param mongo_conn: The instance of the connection to the database to use. :param mongo_conn: The instance of the connection to the database to use.
:param account_identifier: The id of the document in the database that holds the tokens to access the account. :param token_id: The id of the document in the database that holds the tokens to access the account.
Needed only for refreshing the tokens and saving them. Needed only for refreshing the tokens and saving them.
:param llm: The instance of the LLM to use to summarize the mail's content. :param llm: The instance of the LLM to use to summarize the mail's content.
:param force_sync: Whether you would like to forcefully re-sync the mail even if it is already present in the :param force_sync: Whether you would like to forcefully re-sync the mail even if it is already present in the
@@ -508,12 +506,12 @@ class MailSyncModel(BaseModel):
# We first load the authorization tokens: # We first load the authorization tokens:
auth_json = await current_app.mail_oauth_model.get_token( auth_json = await current_app.mail_oauth_model.get_token(
mongo_conn = mongo_conn, mongo_conn = mongo_conn,
account_identifier = account_identifier, token_id = token_id,
) )
# If we failed to load the authorization tokens: # If we failed to load the authorization tokens:
if not auth_json: if not auth_json:
sync_results.message = f"no such account identifier '{account_identifier}'" sync_results.message = f"no such token id '{token_id}'"
return sync_results return sync_results
# ┏┓ ┏┓┳┳┓ •┓ # ┏┓ ┏┓┳┳┓ •┓
@@ -524,7 +522,7 @@ class MailSyncModel(BaseModel):
return await self.__sync_many_gmail( return await self.__sync_many_gmail(
session_token = session_token, session_token = session_token,
mongo_conn = mongo_conn, mongo_conn = mongo_conn,
account_identifier = account_identifier, token_id = token_id,
mail_client = current_app.gmail_client, mail_client = current_app.gmail_client,
tokens = GoogleAuthTokens(**auth_json["token"]), tokens = GoogleAuthTokens(**auth_json["token"]),
llm = llm, llm = llm,
+1 -1
View File
@@ -101,7 +101,7 @@ class MailListRequestHeaders(BaseModel):
class MailListByAccountIdRequestData(BaseModel): class MailListByAccountIdRequestData(BaseModel):
accountId: str = Field( tokenId: str = Field(
description = "the account identifier (Mongo ObjectId) granted by 'MailOAuthModel.get_account_identifier'", description = "the account identifier (Mongo ObjectId) granted by 'MailOAuthModel.get_account_identifier'",
frozen = True frozen = True
) )
+1 -1
View File
@@ -101,7 +101,7 @@ class MailSyncRequestHeaders(BaseModel):
class MailSyncRequestData(BaseModel): class MailSyncRequestData(BaseModel):
accountId: str = Field( tokenId: str = Field(
description = "the account identifier (Mongo ObjectId) granted by 'MailOAuthModel.get_account_identifier'", description = "the account identifier (Mongo ObjectId) granted by 'MailOAuthModel.get_account_identifier'",
frozen = True frozen = True
) )
View File
+188
View File
@@ -0,0 +1,188 @@
"""
AUTHOR:
Khushal P Soonderji
DATE:
Thursday, 5th Dec., 2024.
OBJECTIVE:
To provide a structure to receive auth details of various SMS providers.
REFERENCES:
N/A
DOWNLOADS:
N/A
"""
# *****************************************************************************************************************
# ***** ****
# *** IMPORT ***
# ***** ****
# *****************************************************************************************************************
# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")
# For making data behaviour_models:
from pydantic import BaseModel, Field, field_validator, PastDatetime
from typing import Optional, Literal, Union
# My utils:
from utils_v2.string import regex
from utils_v2.date_time import date_time
# To work with date and time:
import datetime
# *****************************************************************************************************************
# ***** ****
# *** MACROS / ONE-TIME INIT ***
# ***** ****
# *****************************************************************************************************************
# RegEx Patterns:
REGEX_SESSION_TOKEN = r"^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"
# *****************************************************************************************************************
# ***** ****
# *** VARIABLES ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** FUNCTIONS ***
# ***** ****
# *****************************************************************************************************************
class NimbusSMSIndiaAuth(BaseModel):
entityId: str = Field(
description = "the entity id as registered with DLT",
frozen = True
)
senderId: str = Field(
description = "the 6-char code that you see in your SMS inbox",
frozen = True,
examples = ["HDFCBK", "NSESMS", "ZRODHA"]
)
userId: str = Field(
description = "the 6-digit id that Nimbus has assigned to you",
frozen = True
)
apiKey: str = Field(
description = "the key generated through Nimbus's portal",
frozen = True
)
# ┏┓ ┏•
# ┃ ┏┓┏┓╋┓┏┓
# ┗┛┗┛┛┗┛┗┗┫
# ┛
class Config:
extra = "forbid"
# ---------------------------------------------------------------------------------------------------------------------
class SavvyBulkSMSKenyaAuth(BaseModel):
apiKey: str = Field(
description = "the key generated through Savvy's portal",
frozen = True
)
partnerId: str = Field(
description = "the key generated through Savvy's portal",
frozen = True
)
shortCode: str = Field(
description = "your short code with Savvy",
frozen = True
)
# ┏┓ ┏•
# ┃ ┏┓┏┓╋┓┏┓
# ┗┛┗┛┛┗┛┗┗┫
# ┛
class Config:
extra = "forbid"
# ---------------------------------------------------------------------------------------------------------------------
class SMSAuthRequestHeaders(BaseModel):
sessionToken: str = Field(
description = "the session token of the user who is requesting the service",
pattern = REGEX_SESSION_TOKEN,
frozen = True,
alias = "X-Session-Token"
)
# ┏┓ ┏•
# ┃ ┏┓┏┓╋┓┏┓
# ┗┛┗┛┛┗┛┗┗┫
# ┛
class Config:
extra = "allow"
def model_dump(self, *args, **kwargs):
return super().model_dump(*args, by_alias = True, **kwargs)
# ---------------------------------------------------------------------------------------------------------------------
class SMSAuthRequestData(BaseModel):
client: Union[NimbusSMSIndiaAuth, SavvyBulkSMSKenyaAuth]
# ┏┓ ┏•
# ┃ ┏┓┏┓╋┓┏┓
# ┗┛┗┛┛┗┛┗┗┫
# ┛
class Config:
extra = "forbid"
# *****************************************************************************************************************
# ***** ****
# *** MAIN PROGRAM ***
# ***** ****
# *****************************************************************************************************************
if __name__ == "__main__":
pass
+4 -4
View File
@@ -84,10 +84,10 @@ class AsyncNimbusSMS:
def __init__( def __init__(
self, self,
entity_id, entity_id: str,
sender_id, sender_id: str,
user_id, user_id: str,
api_key, api_key: str,
http_client: httpx.AsyncClient = None, http_client: httpx.AsyncClient = None,
debug = True, debug = True,
debug_prefix = "Nimbus SMS | ", debug_prefix = "Nimbus SMS | ",