(20241221) Zerodha Auth Ready. Users can now integrate Kite.

This commit is contained in:
2024-12-21 15:22:55 +05:30
parent 8904ea394d
commit 1249841b05
15 changed files with 568 additions and 464 deletions
@@ -59,18 +59,15 @@ from utils_v2.api.async_quart import (
handle_cancelled_request
)
# GMail-related utils:
from utils_v2.goog.controllers.gmail.gmail_client import SCOPES_GMAIL_MAIL_MANAGEMENT
# Common:
from shared import constants
# Data Models:
from models.api.mail.oauth import (
OAuthMailAuthorizationRequestHeaders,
OAuthMailAuthorizationRequestData
)
from models.core.auth_token import CoreAuthTokenModel
from models.api.finstitutions.trading.auth import (
TradingAuthRequestHeaders,
TradingAuthRequestData
)
# For asynchronous activities:
import asyncio
@@ -131,13 +128,13 @@ def init(blueprint_setup_state):
@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)
header_validator = lambda x: TradingAuthRequestHeaders(**x).model_dump(),
data_validator = lambda x: TradingAuthRequestData(**x)
)
@handle_cancelled_request()
async def request_oauth_authorization_url(
inbound_headers: dict | OAuthMailAuthorizationRequestHeaders = None,
inbound_data: dict | OAuthMailAuthorizationRequestData = None,
inbound_headers: dict | TradingAuthRequestHeaders = None,
inbound_data: dict | TradingAuthRequestData = None,
inbound_files: dict = None,
**kwargs
):
@@ -168,48 +165,47 @@ async def request_oauth_authorization_url(
# Start by assuming failure:
auth_url = None
# ┏┓ ┏┳┓ ┓ ┓┏┓
# ┃┓┏┓┏┓┏┓┏┓┏┓┏┓ ┃ ┏┓┃┏┏┓┏┓ ┃┫ ┏┓┓┏
# ┗┛┗ ┛┗ ┛ ┗┻┗┗ ┻ ┗┛┛┗┗ ┛┗ ┛┗┛┗ ┗┫
# ┛
# ┏┓ ┏┓ ┓┓ ┓┏┓
# ┏┓┏┓ ┏┛┏┓┏┓┏┓┏┫┣┓┏┓ ┃┫ ┓╋┏┓
# ┻ ┗┛┛ ┗┛┗ ┛ ┗┛┗┻┗┗┻ ┛┗┛┗┗┗
# Make a user identifier from the session info:
token_key = await current_app.mail_controller.generate_token_key(
db_conn = current_app.sql_writer,
mongo_conn = current_app.data_mongo,
auth_token = CoreAuthTokenModel(
serviceType = "email",
client = inbound_data.mailClient,
authType = "oauth",
user = kwargs["session_info"],
clientUserId = {"email": inbound_data.mailId},
status = "pending",
syncFreq = inbound_data.syncFreq,
),
session_token = inbound_headers["X-Session-Token"]
)
if token_key is None:
return ResponseModel(
status_code = StatusCodes.FAILED,
http_code = HttpCodes.INTERNAL_SERVER_ERROR,
message = "Failed to generate token key."
# PLANNED FLOW FOR ZERODHA-KITE:
# Step 01.: (One time) The user will go to the integrations page and add his API Key and SPI Secret there. We store
# these values without verification.
# Step 02.: (Daily) The user will go to the investments tab and click on his Zerodha account, which will give him
# a URL that will take him to Zerodha's official site to log in. When he logs in, Zerodha will hit our
# callback URL and give us the authentication details.
if inbound_data.client == "zerodhaKite":
# Prepare the inputs:
auth_url = await current_app.zerodha_kite_controller.get_authorization_url(api_key = inbound_data.auth.apiKey)
# Immediately save the details against that token id:
success = await current_app.zerodha_kite_controller.set_token_direct(
sql_conn = current_app.sql_writer,
mongo_data_conn = current_app.data_mongo,
auth_token = CoreAuthTokenModel(
serviceType = "stockTrading",
client = inbound_data.client,
authType = "oauth",
user = kwargs["session_info"],
clientUserId = {
"apiKey": inbound_data.auth.apiKey
},
auth = inbound_data.auth.model_dump(),
status = "active",
syncFreq = 1500
),
token_notes = {
"apiKey": inbound_data.auth.apiKey,
"authUrl": auth_url
},
session_token = inbound_headers["X-Session-Token"]
)
# ┏┓ ┏┓┳┳┓ •┓
# ┣ ┏┓┏┓ ┃┓┃┃┃┏┓┓┃
# ┻ ┗┛┛ ┗┛┛ ┗┗┻┗┗
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 = str(token_key),
access_type = "offline",
approval_prompt = "force",
include_granted_scopes = "true",
user_email = inbound_data.mailId
)
# Check if things were successful:
if not success: auth_url = None
# ┳┓
# ┣┫┏┓┏┏┓┏┓┏┓┏┏┓
@@ -221,7 +217,7 @@ async def request_oauth_authorization_url(
status_code = StatusCodes.OK if auth_url else StatusCodes.FAILED,
http_code = HttpCodes.SUCCESS if auth_url else HttpCodes.INTERNAL_SERVER_ERROR,
data = {
"client": inbound_data.mailClient,
"client": inbound_data.client,
"authorizationUrl": auth_url
}
)