(20250104) Breeze authorization will be accepted now.
This commit is contained in:
@@ -61,9 +61,6 @@ from utils_v2.api.async_quart import (
|
||||
handle_failed_request
|
||||
)
|
||||
|
||||
# GMail-related utils:
|
||||
from utils_v2.goog.controllers.gmail.gmail_client import SCOPES_GMAIL_MAIL_MANAGEMENT
|
||||
|
||||
# Data Models:
|
||||
from models.core.auth_token import CoreAuthTokenModel
|
||||
|
||||
@@ -124,17 +121,19 @@ async def handle_auth_exception():
|
||||
return await render_template(
|
||||
"/finstitutions/trading/oauth/oauth_failure_v2.html",
|
||||
client = g.client_label,
|
||||
failure_hint = (
|
||||
f"Something went wrong (E). "
|
||||
failure_hint = " ".join([
|
||||
f"Something went wrong (E).",
|
||||
g.client_response.message,
|
||||
f"Please use log-id <b>'{g.log_id}'</b> to check with the support team."
|
||||
)
|
||||
])
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@trading_oauth_callback_bp.route("/callback/<trading_client>", methods = ["POST", "GET"])
|
||||
@trading_oauth_callback_bp.route("/callback/<trading_client>", methods = ["GET", "POST"])
|
||||
@trading_oauth_callback_bp.route("/callback/<trading_client>/<client_user_id>", methods = ["GET", "POST"])
|
||||
@set_api_version(api_version = "1.0.0")
|
||||
@read_input(sanitize_headers = False, sanitize_data = False)
|
||||
@log_request_to_mongo(
|
||||
@@ -152,6 +151,7 @@ async def handle_auth_exception():
|
||||
@handle_failed_request(cleanup_coro = handle_auth_exception)
|
||||
async def trading_oauth_callback(
|
||||
trading_client: str = None,
|
||||
client_user_id: str = None,
|
||||
inbound_headers: dict = None,
|
||||
inbound_data: dict = None,
|
||||
inbound_files: dict = None,
|
||||
@@ -162,6 +162,7 @@ async def trading_oauth_callback(
|
||||
This endpoint gets triggered by the stockbroker's servers to let you know when a user accepted or rejected an
|
||||
authorization request.
|
||||
:param trading_client: The name of the stockbroker that you have received the callback from.
|
||||
:param client_user_id: How the trading client identifies this user.
|
||||
:param inbound_headers: auto-extracted by the decorators.
|
||||
:param inbound_data: auto-extracted by the decorators.
|
||||
:param inbound_files: auto-extracted by the decorators.
|
||||
@@ -174,20 +175,31 @@ async def trading_oauth_callback(
|
||||
|
||||
# Store needed values in 'g':
|
||||
g.log_id = kwargs.get("log_id")
|
||||
g.client_label = "Zerodha (Kite)"
|
||||
g.client_label_map = {
|
||||
"zerodhaKite": "Zerodha (Kite)",
|
||||
"iciciBreeze": "ICICI (Breeze)"
|
||||
}
|
||||
|
||||
# Start by assuming failure:
|
||||
success = None
|
||||
g.client_response = None
|
||||
|
||||
# ┏┓ ┏┓ ┓┓ ┓┏┓•
|
||||
# ┣ ┏┓┏┓ ┏┛┏┓┏┓┏┓┏┫┣┓┏┓ ┃┫ ┓╋┏┓
|
||||
# ┻ ┗┛┛ ┗┛┗ ┛ ┗┛┗┻┛┗┗┻ ┛┗┛┗┗┗
|
||||
# ┳┓ ┓ ┏┓ ┓ •
|
||||
# ┣┫┏┓┏┓┃┏┏┓┏┓ ┗┓┏┓┃┏┓┏╋┓┏┓┏┓
|
||||
# ┻┛┛ ┗┛┛┗┗ ┛ ┗┛┗ ┗┗ ┗┗┗┗┛┛┗
|
||||
|
||||
if trading_client == "zerodha":
|
||||
success = await current_app.zerodha_kite_controller.handle_authorization_callback(
|
||||
g.client_label = g.client_label_map.get(trading_client, trading_client)
|
||||
|
||||
match trading_client:
|
||||
case "zerodhaKite": client_controller = current_app.zerodha_kite_controller
|
||||
case "iciciBreeze": client_controller = current_app.icici_breeze_controller
|
||||
case _: client_controller = None
|
||||
|
||||
if client_controller is not None:
|
||||
g.client_response = await client_controller.handle_authorization_callback(
|
||||
sql_conn = current_app.sql_writer,
|
||||
mongo_data_conn = current_app.data_mongo,
|
||||
inbound_data = inbound_data
|
||||
inbound_data = inbound_data,
|
||||
client_user_id = client_user_id
|
||||
)
|
||||
|
||||
# ┳┓
|
||||
@@ -195,30 +207,35 @@ async def trading_oauth_callback(
|
||||
# ┛┗┗ ┛┣┛┗┛┛┗┛┗
|
||||
# ┛
|
||||
|
||||
# No valid client:
|
||||
if success is None: return await render_template(
|
||||
"/finstitutions/trading/oauth/oauth_failure_v2.html",
|
||||
client = g.client_label,
|
||||
failure_hint = (
|
||||
f"Invalid client '{g.client_label}' selected. "
|
||||
f"Please use log-id '{g.log_id}' to check with the support team."
|
||||
# Prepare a set of warning messages:
|
||||
callback_url_upgrade_warning = (
|
||||
"<br><br><b>WARNING: You are using the old callback system which could be discontinued at any time. "
|
||||
"Please update the callback URL on your broker's portal to include your user id in it.</b>"
|
||||
)
|
||||
|
||||
# No valid client:
|
||||
if client_controller is None: return await render_template(
|
||||
"/finstitutions/trading/oauth/oauth_failure_v2.html",
|
||||
failure_hint = " ".join([
|
||||
f"Invalid client '{g.client_label}' selected.",
|
||||
f"Please use log-id <b>'{g.log_id}'</b> to check with the support team."
|
||||
])
|
||||
)
|
||||
|
||||
# Successful auth:
|
||||
if success: return await render_template(
|
||||
if g.client_response.success: return await render_template(
|
||||
"/finstitutions/trading/oauth/oauth_success_v2.html",
|
||||
client = g.client_label
|
||||
client = g.client_label,
|
||||
extra_message = callback_url_upgrade_warning if client_user_id is None else ""
|
||||
)
|
||||
|
||||
# Failed auth:
|
||||
if success is None: return await render_template(
|
||||
if g.client_response.success is False: return await render_template(
|
||||
"/finstitutions/trading/oauth/oauth_failure_v2.html",
|
||||
client = g.client_label,
|
||||
failure_hint = (
|
||||
f"Something went wrong (NE). "
|
||||
f"Please use log-id '{g.log_id}' to check with the support team."
|
||||
)
|
||||
failure_hint = " ".join([
|
||||
g.client_response.message,
|
||||
f"Please use log-id <b>'{g.log_id}'</b> to check with the support team."
|
||||
])
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -171,6 +171,9 @@ async def request_oauth_authorization_url(
|
||||
# ┻ ┗┛┛ ┣┛┗┻┣┛┗ ┛ ┻ ┛ ┗┻┗┻┗┛┗┗┫
|
||||
# ┛ ┛
|
||||
|
||||
# This is an internal paper-trading account.
|
||||
# It won't need daily logins for usage.
|
||||
|
||||
if inbound_data.client == "paperTrading":
|
||||
|
||||
# Immediately save the details against that token id:
|
||||
@@ -187,7 +190,7 @@ async def request_oauth_authorization_url(
|
||||
},
|
||||
auth = inbound_data.auth.model_dump(),
|
||||
status = "active",
|
||||
syncFreq = 1500
|
||||
syncFreq = None
|
||||
),
|
||||
token_notes = {
|
||||
"username": inbound_data.auth.username
|
||||
@@ -205,7 +208,7 @@ async def request_oauth_authorization_url(
|
||||
# ┻ ┗┛┛ ┗┛┗ ┛ ┗┛┗┻┛┗┗┻ ┛┗┛┗┗┗
|
||||
|
||||
# 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
|
||||
# Step 01.: (One time) The user will go to the integrations page and add his API Key and API 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
|
||||
@@ -226,17 +229,19 @@ async def request_oauth_authorization_url(
|
||||
authType = "oauth",
|
||||
user = kwargs["session_info"],
|
||||
clientUserId = {
|
||||
"userId": inbound_data.auth.userId,
|
||||
"apiKey": inbound_data.auth.apiKey
|
||||
},
|
||||
auth = inbound_data.auth.model_dump(),
|
||||
status = "active",
|
||||
syncFreq = 1500
|
||||
syncFreq = None
|
||||
),
|
||||
token_notes = {
|
||||
"userId": inbound_data.auth.userId,
|
||||
"apiKey": inbound_data.auth.apiKey,
|
||||
"authUrl": auth_url
|
||||
},
|
||||
display_name = None,
|
||||
display_name = inbound_data.auth.userId,
|
||||
display_picture = None,
|
||||
session_token = inbound_headers["X-Session-Token"]
|
||||
)
|
||||
@@ -248,6 +253,13 @@ async def request_oauth_authorization_url(
|
||||
# ┣ ┏┓┏┓ ┃┃ ┃┃ ┃ ┣┫┏┓┏┓┏┓┓┏┓
|
||||
# ┻ ┗┛┛ ┻┗┛┻┗┛┻ ┻┛┛ ┗ ┗ ┗┗
|
||||
|
||||
# PLANNED FLOW FOR ICICI-BREEZE:
|
||||
# Step 01.: (One time) The user will go to the integrations page and add his Client User ID, API Key, and API Secret
|
||||
# there. We store these values without verification.
|
||||
# Step 02.: (Daily) The user will go to the investments tab and click on his Breeze account, which will give him a
|
||||
# URL that will take him to ICICI's official site to log in. When he logs in, ICICI will hit our callback
|
||||
# URL and give us the authentication details.
|
||||
|
||||
if inbound_data.client == "iciciBreeze":
|
||||
|
||||
# Prepare the inputs:
|
||||
@@ -263,17 +275,19 @@ async def request_oauth_authorization_url(
|
||||
authType = "oauth",
|
||||
user = kwargs["session_info"],
|
||||
clientUserId = {
|
||||
"userId": inbound_data.auth.userId,
|
||||
"apiKey": inbound_data.auth.apiKey
|
||||
},
|
||||
auth = inbound_data.auth.model_dump(),
|
||||
status = "active",
|
||||
syncFreq = 1500
|
||||
syncFreq = None
|
||||
),
|
||||
token_notes = {
|
||||
"userId": inbound_data.auth.userId,
|
||||
"apiKey": inbound_data.auth.apiKey,
|
||||
"authUrl": auth_url
|
||||
},
|
||||
display_name = None,
|
||||
display_name = inbound_data.auth.userId,
|
||||
display_picture = None,
|
||||
session_token = inbound_headers["X-Session-Token"]
|
||||
)
|
||||
|
||||
@@ -46,6 +46,7 @@ from controllers_v2.finstitutions.trading.base import TradingController
|
||||
# Models:
|
||||
from models.core.auth_token import CoreAuthTokenModel
|
||||
from models.api.finstitutions.trading.symbols.list import TradingSymbolListRequestData, TradingSymbolListBrokerResponse
|
||||
from models.finstitutions.trading.oauth import TradingOAuthCallbackResponse
|
||||
|
||||
# To work with datatypes:
|
||||
from typing import List, Any
|
||||
@@ -150,8 +151,9 @@ class AllTradingController(TradingController):
|
||||
self,
|
||||
sql_conn: AsyncMySQL,
|
||||
mongo_data_conn: AsyncMongo,
|
||||
inbound_data: dict
|
||||
) -> bool:
|
||||
inbound_data: dict,
|
||||
client_user_id: str
|
||||
) -> TradingOAuthCallbackResponse:
|
||||
"""
|
||||
When the end user interacts with their broker's APIs, the broker's servers would usually issue a callback.
|
||||
We've seen this in the case of Zerodha Kite and ICICI Breeze. Use this method to handle the callback loop to
|
||||
@@ -159,7 +161,8 @@ class AllTradingController(TradingController):
|
||||
:param sql_conn: The database connection to use to perform this activity.
|
||||
:param mongo_data_conn: The database connection to use to perform this activity.
|
||||
:param inbound_data: The data that came in from the broker. This could be in the JSON body, query params, etc.
|
||||
:return: True if the callback loop was completed successfully, else False.
|
||||
:param client_user_id: How the trading client identifies this user.
|
||||
:return: A structured response to capture the process of callback handling.
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -46,6 +46,7 @@ from controllers_v2.core.auth_token import CoreAuthTokenController
|
||||
# Models:
|
||||
from models.core.auth_token import CoreAuthTokenModel
|
||||
from models.api.finstitutions.trading.symbols.list import TradingSymbolListRequestData, TradingSymbolListBrokerResponse
|
||||
from models.finstitutions.trading.oauth import TradingOAuthCallbackResponse
|
||||
|
||||
# To work with datatypes:
|
||||
from typing import List, Any
|
||||
@@ -170,8 +171,9 @@ class TradingController(CoreAuthTokenController, ABC):
|
||||
self,
|
||||
sql_conn: AsyncMySQL,
|
||||
mongo_data_conn: AsyncMongo,
|
||||
inbound_data: dict
|
||||
) -> bool:
|
||||
inbound_data: dict,
|
||||
client_user_id: str
|
||||
) -> TradingOAuthCallbackResponse:
|
||||
|
||||
"""
|
||||
When the end user interacts with their broker's APIs, the broker's servers would usually issue a callback.
|
||||
@@ -180,7 +182,8 @@ class TradingController(CoreAuthTokenController, ABC):
|
||||
:param sql_conn: The database connection to use to perform this activity.
|
||||
:param mongo_data_conn: The database connection to use to perform this activity.
|
||||
:param inbound_data: The data that came in from the broker. This could be in the JSON body, query params, etc.
|
||||
:return: True if the callback loop was completed successfully, else False.
|
||||
:param client_user_id: How the trading client identifies this user.
|
||||
:return: A structured response to capture the process of callback handling.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@@ -51,6 +51,7 @@ from models.api.finstitutions.trading.symbols.list import (
|
||||
TradingSymbolListBrokerResponse,
|
||||
TradingSymbol
|
||||
)
|
||||
from models.finstitutions.trading.oauth import TradingOAuthCallbackResponse
|
||||
|
||||
# To work with MongoDB:
|
||||
from bson.objectid import ObjectId
|
||||
@@ -62,8 +63,8 @@ from typing import List, Any
|
||||
import httpx
|
||||
import urllib
|
||||
|
||||
# To work with Zerodha's Kite platform:
|
||||
from kiteconnect import KiteConnect
|
||||
# To work with ICICI Breeze's platform:
|
||||
from breeze_connect import BreezeConnect
|
||||
|
||||
# To handle exceptions:
|
||||
from pydantic import ValidationError
|
||||
@@ -179,8 +180,9 @@ class ICICIBreezeTradingController(TradingController):
|
||||
self,
|
||||
sql_conn: AsyncMySQL,
|
||||
mongo_data_conn: AsyncMongo,
|
||||
inbound_data: dict
|
||||
) -> bool:
|
||||
inbound_data: dict,
|
||||
client_user_id: str
|
||||
) -> TradingOAuthCallbackResponse:
|
||||
|
||||
"""
|
||||
To capture the callback from ICICI Breeze's authorization loop. This happens when the user successfully logs in
|
||||
@@ -188,7 +190,8 @@ class ICICIBreezeTradingController(TradingController):
|
||||
:param sql_conn: The database connection to use to perform this activity.
|
||||
:param mongo_data_conn: The database connection to use to perform this activity.
|
||||
:param inbound_data: The data that came in from the broker. This could be in the JSON body, query params, etc.
|
||||
:return: True if the callback loop was completed successfully, else False..
|
||||
:param client_user_id: How the trading client identifies this user.
|
||||
:return: A structured response to capture the process of callback handling.
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -52,6 +52,7 @@ from models.api.finstitutions.trading.symbols.list import (
|
||||
TradingSymbolListBrokerResponse,
|
||||
TradingSymbol
|
||||
)
|
||||
from models.finstitutions.trading.oauth import TradingOAuthCallbackResponse
|
||||
|
||||
# To work with MongoDB:
|
||||
from bson.objectid import ObjectId
|
||||
@@ -179,15 +180,17 @@ class PaperTradingController(TradingController):
|
||||
self,
|
||||
sql_conn: AsyncMySQL,
|
||||
mongo_data_conn: AsyncMongo,
|
||||
inbound_data: dict
|
||||
) -> bool:
|
||||
inbound_data: dict,
|
||||
client_user_id: str
|
||||
) -> TradingOAuthCallbackResponse:
|
||||
|
||||
"""
|
||||
Not needed for paper trading.
|
||||
:param sql_conn: The database connection to use to perform this activity.
|
||||
:param mongo_data_conn: The database connection to use to perform this activity.
|
||||
:param inbound_data: The data that came in from the broker. This could be in the JSON body, query params, etc.
|
||||
:return: True if the callback loop was completed successfully, else False..
|
||||
:param client_user_id: How the trading client identifies this user.
|
||||
:return: A structured response to capture the process of callback handling.
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -52,6 +52,7 @@ from models.api.finstitutions.trading.symbols.list import (
|
||||
TradingSymbolListBrokerResponse,
|
||||
TradingSymbol
|
||||
)
|
||||
from models.finstitutions.trading.oauth import TradingOAuthCallbackResponse
|
||||
|
||||
# To work with MongoDB:
|
||||
from bson.objectid import ObjectId
|
||||
@@ -179,50 +180,79 @@ class ZerodhaKiteTradingController(TradingController):
|
||||
self,
|
||||
sql_conn: AsyncMySQL,
|
||||
mongo_data_conn: AsyncMongo,
|
||||
inbound_data: dict
|
||||
) -> bool:
|
||||
inbound_data: dict,
|
||||
client_user_id: str
|
||||
) -> TradingOAuthCallbackResponse:
|
||||
|
||||
"""
|
||||
When the end user interacts with Zerodha's APIs, Zerodha's servers issue a callback like this:
|
||||
http://127.0.0.1:5999/auth/callback?action=login&type=login&status=success&request_token=the-request-token
|
||||
We must use the request token to get the access token. The access token is the thing that we must hold onto for
|
||||
executing actual actions like subscribing to live market feed, placing trades, etc.
|
||||
NOTE: Please ensure that you set the 'Redirect URL' such that is passes back Kite's 'api_key' back through the
|
||||
callback URL. This can be one by setting the value manually as a query param on the app's configuration
|
||||
page. E.g.: http://127.0.0.1:5999/auth/callback?api_key=user_api_key
|
||||
NOTE: Please ensure that you set the 'Redirect URL' in the format as shown below:
|
||||
01. http://127.0.0.1:5106/converse/finstitutions/trading/oauth/callback/zerodhaKite/<client_user_id>
|
||||
02. https://api.thecaoffice.com/converse/finstitutions/trading/oauth/callback/zerodhaKite/<client_user_id>
|
||||
BACKWARD COMPATIBILITY:
|
||||
Earlier, we used to set the same API key in the callback URL as a query param like shown below:
|
||||
E.g.: http://127.0.0.1:5106/converse/finstitutions/trading/oauth/callback/zerodhaKite?api_key=user_api_key
|
||||
:param sql_conn: The database connection to use to perform this activity.
|
||||
:param mongo_data_conn: The database connection to use to perform this activity.
|
||||
:param inbound_data: The data that came in from the broker. This could be in the JSON body, query params, etc.
|
||||
:return: True if the callback loop was completed successfully, else False..
|
||||
:param client_user_id: How the trading client identifies this user.
|
||||
:return: A structured response to capture the process of callback handling.
|
||||
"""
|
||||
|
||||
# Start by assuming failure:
|
||||
success = False
|
||||
response = TradingOAuthCallbackResponse()
|
||||
zerodha_auth_token = None
|
||||
|
||||
# Check if either the new system or the old system is being followed.
|
||||
# At least one is needed:
|
||||
api_key = inbound_data.get("api_key")
|
||||
if not api_key and not client_user_id:
|
||||
response.message = "Your callback URL hasn't been configured properly."
|
||||
return response
|
||||
|
||||
# Get the token from the database:
|
||||
old_condition = mongo_data_conn.dict_to_dot_notation({"auth": {"apiKey": api_key}})
|
||||
condition = mongo_data_conn.dict_to_dot_notation({"auth": {"userId": client_user_id}})
|
||||
auth_token = await self.get_token_from_filter(
|
||||
mongo_data_conn = mongo_data_conn,
|
||||
filter_json = mongo_data_conn.dict_to_dot_notation({
|
||||
"auth": {
|
||||
"apiKey": inbound_data.get(
|
||||
"api_key",
|
||||
"Hint: Put the user's app's key in the query params of the 'Redirect URL'"
|
||||
)
|
||||
}
|
||||
})
|
||||
filter_json = {"$or": [old_condition, condition]}
|
||||
)
|
||||
|
||||
# If not such auth token exists:
|
||||
if not auth_token: return success
|
||||
if not auth_token:
|
||||
response.message = (
|
||||
f"No such integration found in our system. "
|
||||
"Please add this integration first and then try again."
|
||||
)
|
||||
return response
|
||||
|
||||
# Get the final access tokens set from Zerodha Kite:
|
||||
try:
|
||||
kite = KiteConnect(api_key = auth_token.auth["apiKey"])
|
||||
session_data = kite.generate_session(
|
||||
request_token = inbound_data["request_token"],
|
||||
api_secret = auth_token.auth["apiSecret"]
|
||||
)
|
||||
zerodha_auth_token = ZerodhaKiteAuthTokens(**session_data)
|
||||
except Exception as exception:
|
||||
response.exception = exception
|
||||
response.message = str(exception)
|
||||
return response
|
||||
|
||||
# Ensure that the client user id of the incoming callback and the one given in Zerodha's session data match:
|
||||
if (
|
||||
client_user_id is not None and # ................ For backward compatibility.
|
||||
zerodha_auth_token.userId != client_user_id # ... New mechanism that verifies account match.
|
||||
):
|
||||
response.message = (
|
||||
f"We were expecting authorization for the account '{client_user_id}', "
|
||||
f"but Zerodha says the authorization was granted for the account '{zerodha_auth_token.userId}'. "
|
||||
"This could be because of a misconfigured callback URL."
|
||||
)
|
||||
return response
|
||||
|
||||
# Prepare the inputs to save to the database:
|
||||
auth_url = await self.get_authorization_url(api_key = auth_token.auth["apiKey"])
|
||||
@@ -242,8 +272,15 @@ class ZerodhaKiteTradingController(TradingController):
|
||||
display_picture = zerodha_auth_token.displayPictureUrl
|
||||
)
|
||||
|
||||
# If saving the token fails:
|
||||
if not success:
|
||||
response.message = "Something went wrong towards the end of the authorization cycle."
|
||||
return response
|
||||
|
||||
# Done here:
|
||||
return success
|
||||
response.success = True
|
||||
response.message = "Authorization cycle successfully completed."
|
||||
return response
|
||||
|
||||
# ┏┳┓ ┓• ┏┓ ┓ ┓
|
||||
# ┃ ┏┓┏┓┏┫┓┏┓┏┓ ┗┓┓┏┏┳┓┣┓┏┓┃┏
|
||||
|
||||
@@ -101,18 +101,24 @@ class PaperTradingAuth(BaseModel):
|
||||
|
||||
class ZerodhaKiteAuth(BaseModel):
|
||||
|
||||
userId: str = Field(
|
||||
description = "How Zerodha's Kite platform identifies this user.",
|
||||
frozen = True,
|
||||
alias = "clientId"
|
||||
)
|
||||
|
||||
apiKey: str = Field(
|
||||
description = (
|
||||
"the api key of your kite connect app; "
|
||||
"this remains constant throughout the life of the app"
|
||||
"The API key of your Kite app. "
|
||||
"This remains constant throughout the life of the app."
|
||||
),
|
||||
frozen = True
|
||||
)
|
||||
|
||||
apiSecret: str = Field(
|
||||
description = (
|
||||
"the api secret of your kite connect app; "
|
||||
"this can change if you think the security of your app has been compromised"
|
||||
"The API secret of your Kite app. "
|
||||
"This can be changed if you think the security of your app has been compromised."
|
||||
),
|
||||
frozen = True
|
||||
)
|
||||
@@ -124,6 +130,7 @@ class ZerodhaKiteAuth(BaseModel):
|
||||
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
populate_by_name = True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -131,13 +138,25 @@ class ZerodhaKiteAuth(BaseModel):
|
||||
|
||||
class ICICIBreezeAuth(BaseModel):
|
||||
|
||||
userId: str = Field(
|
||||
description = "How ICICI's Breeze platform identifies this user.",
|
||||
frozen = True,
|
||||
alias = "clientId"
|
||||
)
|
||||
|
||||
apiKey: str = Field(
|
||||
description = "??",
|
||||
description = (
|
||||
"The API key of your Breeze app. "
|
||||
"This remains constant throughout the life of the app."
|
||||
),
|
||||
frozen = True
|
||||
)
|
||||
|
||||
apiSecret: str = Field(
|
||||
description = "??",
|
||||
description = (
|
||||
"The API secret of your Breeze app. "
|
||||
"This can be changed if you think the security of your app has been compromised."
|
||||
),
|
||||
frozen = True
|
||||
)
|
||||
|
||||
@@ -148,6 +167,7 @@ class ICICIBreezeAuth(BaseModel):
|
||||
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
populate_by_name = True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -179,8 +199,8 @@ class TradingAuthRequestHeaders(BaseModel):
|
||||
|
||||
class TradingAuthRequestData(BaseModel):
|
||||
|
||||
client: Literal["zerodhaKite", "iciciBreeze", "paperTrading"] = Field(alias = "client")
|
||||
auth: Union[ZerodhaKiteAuth, ICICIBreezeAuth, PaperTradingAuth]
|
||||
client: Literal["paperTrading", "zerodhaKite", "iciciBreeze"] = Field(alias = "client")
|
||||
auth: Union[PaperTradingAuth, ZerodhaKiteAuth, ICICIBreezeAuth]
|
||||
|
||||
# ┏┓ ┏•
|
||||
# ┃ ┏┓┏┓╋┓┏┓
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Saturday, 4th Jan., 2025.
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To provide a structure to represent OAuth callback responses.
|
||||
|
||||
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, model_validator, AwareDatetime
|
||||
from typing import Optional, Literal, Union, List, Any
|
||||
|
||||
# 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 ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
class TradingOAuthCallbackResponse(BaseModel):
|
||||
|
||||
success: bool = Field(
|
||||
description = "To indicate whether or not, the action was a success",
|
||||
frozen = False,
|
||||
default = False
|
||||
)
|
||||
|
||||
message: str = Field(
|
||||
description = "To explain what happened in the process of handling the OAuth callback.",
|
||||
frozen = False,
|
||||
default = "ERR: Message not captured."
|
||||
)
|
||||
|
||||
exception: Any = Field(
|
||||
description = "To pass on any exception that occurred in the process.",
|
||||
frozen = False,
|
||||
default = None
|
||||
)
|
||||
|
||||
# ┏┓ ┏•
|
||||
# ┃ ┏┓┏┓╋┓┏┓
|
||||
# ┗┛┗┛┛┗┛┗┗┫
|
||||
# ┛
|
||||
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
# ┏┓ ┏┓
|
||||
# ┃ ┓┏┏╋┏┓┏┳┓ ┣ ┓┏┏┓┏┏
|
||||
# ┗┛┗┻┛┗┗┛┛┗┗ ┻ ┗┻┛┗┗┛
|
||||
|
||||
pass
|
||||
|
||||
# ┓┏ ┓• ┓ •
|
||||
# ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓
|
||||
# ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pass
|
||||
@@ -101,9 +101,11 @@
|
||||
<div class="message-container">
|
||||
<div class="failure-circle">✘</div> <!-- Red circle with failure icon -->
|
||||
<h1><b>Authorization Failed</b></h1>
|
||||
<p>Something went wrong in getting authorization from your <b>{{ client }}</b> account.
|
||||
<p>
|
||||
Something went wrong in getting authorization from your <b>{{ client }}</b> account.
|
||||
<br><br><b>Hint:</b> {{ failure_hint|safe }}<br><br>
|
||||
Please feel free to try the same steps again. You can close this tab at any time.</p>
|
||||
Please feel free to try the same steps again. You can close this tab at any time.
|
||||
</p>
|
||||
|
||||
<!-- Close button -->
|
||||
<button class="btn btn-secondary" onclick="window.close();">Close</button>
|
||||
|
||||
@@ -101,7 +101,11 @@
|
||||
<div class="message-container">
|
||||
<div class="success-circle">✔</div> <!-- Red circle with failure icon -->
|
||||
<h1><b>Authorization Successful</b></h1>
|
||||
<p>We have received authorization from your <b>{{ client }}</b> account. You can close this tab at any time.</p>
|
||||
<p>
|
||||
We have received authorization from your <b>{{ client }}</b> account.
|
||||
{{ extra_message|safe }}
|
||||
<br><br>You can close this tab at any time.
|
||||
</p>
|
||||
|
||||
<!-- Close button -->
|
||||
<button class="btn btn-secondary" onclick="window.close();">Close</button>
|
||||
|
||||
Reference in New Issue
Block a user