(20250104) Breeze authorization cycle ready for testing on live server.
This commit is contained in:
@@ -46,6 +46,7 @@ from controllers_v2.finstitutions.trading.base import TradingController
|
|||||||
|
|
||||||
# Models:
|
# Models:
|
||||||
from models.core.auth_token import CoreAuthTokenModel
|
from models.core.auth_token import CoreAuthTokenModel
|
||||||
|
from utils_v2.trading.icici_breeze.models.auth_tokens import ICICIBreezeAuthTokens
|
||||||
from models.api.finstitutions.trading.symbols.list import (
|
from models.api.finstitutions.trading.symbols.list import (
|
||||||
TradingSymbolListRequestData,
|
TradingSymbolListRequestData,
|
||||||
TradingSymbolListBrokerResponse,
|
TradingSymbolListBrokerResponse,
|
||||||
@@ -194,7 +195,83 @@ class ICICIBreezeTradingController(TradingController):
|
|||||||
:return: A structured response to capture the process of callback handling.
|
:return: A structured response to capture the process of callback handling.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raise NotImplementedError
|
# Start by assuming failure:
|
||||||
|
response = TradingOAuthCallbackResponse()
|
||||||
|
icici_auth_token = None
|
||||||
|
|
||||||
|
# Check if the callback URL was configured properly:
|
||||||
|
if not client_user_id:
|
||||||
|
response.message = "Your callback URL hasn't been configured properly."
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Get the token from the database:
|
||||||
|
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 = condition
|
||||||
|
)
|
||||||
|
|
||||||
|
# If not such auth token exists:
|
||||||
|
if not auth_token:
|
||||||
|
response.message = (
|
||||||
|
f"We couldn't find such an integration in our system. "
|
||||||
|
"Please add this integration first and then try again."
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Get the access token and user information from ICICI Breeze:
|
||||||
|
breeze_session_token = inbound_data.get("apisession")
|
||||||
|
try:
|
||||||
|
breeze = BreezeConnect(api_key = auth_token.auth["apiKey"])
|
||||||
|
breeze.generate_session(
|
||||||
|
api_secret = auth_token.auth["apiSecret"],
|
||||||
|
session_token = breeze_session_token
|
||||||
|
)
|
||||||
|
session_data = breeze.get_customer_details(api_session = breeze_session_token)
|
||||||
|
session_data = session_data.get("Success")
|
||||||
|
icici_auth_token = ICICIBreezeAuthTokens(**session_data)
|
||||||
|
except Exception as exception:
|
||||||
|
response.message = f"Client exception: {exception}"
|
||||||
|
response.exception = exception
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Ensure that the client user id of the incoming callback and the one given in Zerodha's session data match:
|
||||||
|
if icici_auth_token.userId != client_user_id:
|
||||||
|
response.message = (
|
||||||
|
f"We were expecting authorization for the account '{client_user_id}', "
|
||||||
|
f"but ICICI says the authorization was granted for the account '{icici_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"])
|
||||||
|
icici_auth_token.sessionToken = breeze_session_token
|
||||||
|
auth_token.token = icici_auth_token.model_dump()
|
||||||
|
|
||||||
|
# Save the additional auth info to the database:
|
||||||
|
success = await self.set_token(
|
||||||
|
sql_conn = sql_conn,
|
||||||
|
mongo_data_conn = mongo_data_conn,
|
||||||
|
token_key = auth_token.key,
|
||||||
|
auth_token = auth_token,
|
||||||
|
token_notes = {
|
||||||
|
"apiKey": auth_token.auth["apiKey"],
|
||||||
|
"authUrl": auth_url
|
||||||
|
},
|
||||||
|
display_name = icici_auth_token.userId,
|
||||||
|
display_picture = icici_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:
|
||||||
|
response.success = True
|
||||||
|
response.message = "Authorization cycle successfully completed."
|
||||||
|
return response
|
||||||
|
|
||||||
# ┏┳┓ ┓• ┏┓ ┓ ┓
|
# ┏┳┓ ┓• ┏┓ ┓ ┓
|
||||||
# ┃ ┏┓┏┓┏┫┓┏┓┏┓ ┗┓┓┏┏┳┓┣┓┏┓┃┏
|
# ┃ ┏┓┏┓┏┫┓┏┓┏┓ ┗┓┓┏┏┳┓┣┓┏┓┃┏
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ class ZerodhaKiteTradingController(TradingController):
|
|||||||
# If not such auth token exists:
|
# If not such auth token exists:
|
||||||
if not auth_token:
|
if not auth_token:
|
||||||
response.message = (
|
response.message = (
|
||||||
f"No such integration found in our system. "
|
f"We couldn't find such an integration in our system. "
|
||||||
"Please add this integration first and then try again."
|
"Please add this integration first and then try again."
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
@@ -238,8 +238,8 @@ class ZerodhaKiteTradingController(TradingController):
|
|||||||
)
|
)
|
||||||
zerodha_auth_token = ZerodhaKiteAuthTokens(**session_data)
|
zerodha_auth_token = ZerodhaKiteAuthTokens(**session_data)
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
|
response.message = f"Client exception: {exception}"
|
||||||
response.exception = exception
|
response.exception = exception
|
||||||
response.message = str(exception)
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# Ensure that the client user id of the incoming callback and the one given in Zerodha's session data match:
|
# Ensure that the client user id of the incoming callback and the one given in Zerodha's session data match:
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# To work with ICICI Breeze's platform:
|
||||||
|
from breeze_connect import BreezeConnect
|
||||||
|
|
||||||
|
from utils_v2.string import json
|
||||||
|
|
||||||
|
api_key = r"2678j1551CSkQ96I5T1862k1685t0b2d"
|
||||||
|
api_secret = r"kQWD507T9R1169I7AR3473+54@1940P5"
|
||||||
|
session_token = r"50114591"
|
||||||
|
|
||||||
|
print("Init")
|
||||||
|
breeze = BreezeConnect(api_key = api_key)
|
||||||
|
print("Creating session.")
|
||||||
|
breeze.generate_session(
|
||||||
|
api_secret = api_secret,
|
||||||
|
session_token = session_token
|
||||||
|
)
|
||||||
|
|
||||||
|
# Getting Customer details:
|
||||||
|
customer_details = breeze.get_customer_details(api_session = session_token)
|
||||||
|
print(customer_details)
|
||||||
|
print("CUSTOMER:", json.to_string(customer_details, default=str))
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
AUTHOR:
|
||||||
|
|
||||||
|
Khushal P Soonderji
|
||||||
|
|
||||||
|
DATE:
|
||||||
|
|
||||||
|
Saturday, 4th Jan., 2025.
|
||||||
|
|
||||||
|
OBJECTIVE:
|
||||||
|
|
||||||
|
To provide a data model for describing the tokens to be used for ICICI Breeze.
|
||||||
|
|
||||||
|
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, model_validator, AwareDatetime
|
||||||
|
from typing import Optional, Literal, Union, Dict, List, Any
|
||||||
|
|
||||||
|
# Related to Google:
|
||||||
|
from google.auth.transport.requests import Request
|
||||||
|
from google.oauth2.credentials import Credentials
|
||||||
|
|
||||||
|
# My utils:
|
||||||
|
from utils_v2.string import json
|
||||||
|
from utils_v2.string import regex
|
||||||
|
from utils_v2.date_time import date_time
|
||||||
|
|
||||||
|
# To work with date and time:
|
||||||
|
import datetime
|
||||||
|
import dateparser
|
||||||
|
|
||||||
|
# To make API calls:
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MACROS / ONE-TIME INIT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nothing Yet
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** VARIABLES ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nothing Yet
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** FUNCTIONS ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
class ICICIBreezeAuthTokens(BaseModel):
|
||||||
|
|
||||||
|
userId: str = Field(
|
||||||
|
description = "The id of the user as assigned by ICICI Breeze.",
|
||||||
|
alias = "idirect_userid"
|
||||||
|
)
|
||||||
|
|
||||||
|
name: str = Field(
|
||||||
|
description = "The name of the user.",
|
||||||
|
alias = "idirect_user_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
displayName: str | None = Field(
|
||||||
|
description = "The short display name of the user.",
|
||||||
|
default = None,
|
||||||
|
alias = "idirect_user_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
displayPictureUrl: str | None = Field(
|
||||||
|
description = "The display picture of the user.",
|
||||||
|
default = None,
|
||||||
|
alias = "avatar_url"
|
||||||
|
)
|
||||||
|
|
||||||
|
loginTs: AwareDatetime = Field(
|
||||||
|
description = "The time (utc) at which this user logged in.",
|
||||||
|
alias = "idirect_lastlogin_time"
|
||||||
|
)
|
||||||
|
|
||||||
|
sessionToken: str | None = Field(
|
||||||
|
description = "The main access token to be used in actual requests.",
|
||||||
|
default = None
|
||||||
|
)
|
||||||
|
|
||||||
|
# ┏┓ ┏•
|
||||||
|
# ┃ ┏┓┏┓╋┓┏┓
|
||||||
|
# ┗┛┗┛┛┗┛┗┗┫
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = "ignore"
|
||||||
|
populate_by_name = True
|
||||||
|
|
||||||
|
# ┓┏ ┓• ┓ •
|
||||||
|
# ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓
|
||||||
|
# ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗
|
||||||
|
|
||||||
|
@field_validator("loginTs", mode = "before")
|
||||||
|
def parse_dates(cls, value):
|
||||||
|
if not isinstance(value, datetime.datetime):
|
||||||
|
parsed = date_time.parse_date_time(
|
||||||
|
value,
|
||||||
|
date_formats = ["%d-%b-%Y %H:%M:%S", "%Y-%m-%d %H:%M:%S"],
|
||||||
|
timezone = date_time.TIMEZONE_IST
|
||||||
|
)
|
||||||
|
value = parsed if isinstance(parsed, datetime.datetime) else dateparser.parse(value)
|
||||||
|
if isinstance(value, datetime.datetime): value = date_time.to_timezone(value, date_time.TIMEZONE_UTC)
|
||||||
|
return value
|
||||||
|
|
||||||
|
# ┏┓ •
|
||||||
|
# ┃┃┏┓┏┓┏┓┏┓┏┓╋┓┏┓┏
|
||||||
|
# ┣┛┛ ┗┛┣┛┗ ┛ ┗┗┗ ┛
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ┏┓ ┏┓
|
||||||
|
# ┃ ┓┏┏╋┏┓┏┳┓ ┣ ┓┏┏┓┏┏
|
||||||
|
# ┗┛┗┻┛┗┗┛┛┗┗ ┻ ┗┻┛┗┗┛
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MAIN PROGRAM ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
print("BHOPLI & MUCHHI")
|
||||||
|
|
||||||
|
sample_token = {
|
||||||
|
"exg_trade_date": {
|
||||||
|
"NSE": "06-Jan-2025",
|
||||||
|
"BSE": "06-Jan-2025",
|
||||||
|
"FNO": "06-Jan-2025",
|
||||||
|
"NDX": "06-Jan-2025"
|
||||||
|
},
|
||||||
|
"exg_status": {
|
||||||
|
"NSE": "C",
|
||||||
|
"BSE": "C",
|
||||||
|
"FNO": "Y",
|
||||||
|
"NDX": "C"
|
||||||
|
},
|
||||||
|
"segments_allowed": {
|
||||||
|
"Trading": "Y",
|
||||||
|
"Equity": "Y",
|
||||||
|
"Derivatives": "Y",
|
||||||
|
"Currency": "Y"
|
||||||
|
},
|
||||||
|
"idirect_userid": "BHUSHaI4",
|
||||||
|
"idirect_user_name": "BHUSHAN C THAKKAR",
|
||||||
|
"idirect_ORD_TYP": "N",
|
||||||
|
"idirect_lastlogin_time": "04-Jan-2025 15:41:53",
|
||||||
|
"mf_holding_mode_popup_flg": "N",
|
||||||
|
"commodity_exchange_status": "Y",
|
||||||
|
"commodity_trade_date": "06-Jan-2025",
|
||||||
|
"commodity_allowed": "C"
|
||||||
|
}
|
||||||
|
|
||||||
|
model = ICICIBreezeAuthTokens(**sample_token)
|
||||||
|
print(json.to_string(model.model_dump(), default = str))
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
OBJECTIVE:
|
OBJECTIVE:
|
||||||
|
|
||||||
To provide a data model for describing the tokens to be used for Google's APIs.
|
To provide a data model for describing the tokens to be used for Zerodha Kite.
|
||||||
|
|
||||||
REFERENCES:
|
REFERENCES:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user