(20241125) Documentation added.
This commit is contained in:
@@ -112,7 +112,7 @@ def init(blueprint_setup_state):
|
|||||||
attr_name = "logs_mongo",
|
attr_name = "logs_mongo",
|
||||||
project = constants.PROJECT_NAME,
|
project = constants.PROJECT_NAME,
|
||||||
log_type = constants.MODULE_NAME,
|
log_type = constants.MODULE_NAME,
|
||||||
operation = "testCllBckApi",
|
operation = "gmailCllBckApi",
|
||||||
log_input = True,
|
log_input = True,
|
||||||
log_output = True,
|
log_output = True,
|
||||||
sensitive_keys = None
|
sensitive_keys = None
|
||||||
@@ -120,7 +120,7 @@ def init(blueprint_setup_state):
|
|||||||
@log_chain_to_mongo(attr_name = "logs_mongo")
|
@log_chain_to_mongo(attr_name = "logs_mongo")
|
||||||
@should_not_be_under_maintenance(attr_name = "is_under_maintenance")
|
@should_not_be_under_maintenance(attr_name = "is_under_maintenance")
|
||||||
@handle_cancelled_request()
|
@handle_cancelled_request()
|
||||||
async def callback_test(
|
async def mail_callback(
|
||||||
inbound_headers: dict = None,
|
inbound_headers: dict = None,
|
||||||
inbound_data: dict = None,
|
inbound_data: dict = None,
|
||||||
inbound_files: dict = None,
|
inbound_files: dict = None,
|
||||||
@@ -128,7 +128,7 @@ async def callback_test(
|
|||||||
):
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This URL does nothing, just captures data on webhooks and logs it for documentation.
|
Use this when authorizing access to someone's GMail account. This can be used to capture the authentication token.
|
||||||
:param inbound_headers: auto-extracted by the decorators.
|
:param inbound_headers: auto-extracted by the decorators.
|
||||||
:param inbound_data: auto-extracted by the decorators.
|
:param inbound_data: auto-extracted by the decorators.
|
||||||
:param inbound_files: auto-extracted by the decorators.
|
:param inbound_files: auto-extracted by the decorators.
|
||||||
|
|||||||
+49
-4
@@ -125,7 +125,8 @@ class OAuthBase(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def initialize(
|
async def initialize(
|
||||||
self,
|
self,
|
||||||
scopes: List
|
scopes: List,
|
||||||
|
raise_exception = False
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -133,26 +134,70 @@ class OAuthBase(ABC):
|
|||||||
flow that was created for an app through its Cloud Console panel.
|
flow that was created for an app through its Cloud Console panel.
|
||||||
:param scopes: The list of permissions being requested. The word 'scopes' has been borrowed from Google's OAuth
|
:param scopes: The list of permissions being requested. The word 'scopes' has been borrowed from Google's OAuth
|
||||||
documentation (which was implemented first).
|
documentation (which was implemented first).
|
||||||
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
:return: True if the initialization succeeded, False if it failed.
|
:return: True if the initialization succeeded, False if it failed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
async def get_authorization_url(
|
async def get_authorization_url(
|
||||||
self,
|
self,
|
||||||
**kwargs
|
raise_exception = False,
|
||||||
|
**kwargs,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
To create an authorization URL which will be then sent to the front-end for the user to click and grant/decline
|
To create an authorization URL which will be then sent to the front-end for the user to click and grant/decline
|
||||||
various permissions.
|
various permissions.
|
||||||
:param kwargs: The identifiers of the user who wants to use your service (where your service needs access to
|
:param kwargs: The identifiers of the user who wants to use your service and any other service-specific options.
|
||||||
their second-party account).
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
:return: The authorization URL if successful, or None if failed.
|
:return: The authorization URL if successful, or None if failed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def get_tokens(
|
||||||
|
self,
|
||||||
|
raise_exception = False,
|
||||||
|
**kwargs
|
||||||
|
) -> dict | None:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To get the tokens of a user. Plural 'tokens' because OAuth typically has one access token that expires
|
||||||
|
every-so-often, and one refresh token that grants you a new access token.
|
||||||
|
:param kwargs: The identifiers of the user who wants to use your service and any other service-specific options.
|
||||||
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
|
:return: The tokens for the service if successful, or None if failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def refresh_tokens(
|
||||||
|
self,
|
||||||
|
old_tokens: dict,
|
||||||
|
force_refresh = False,
|
||||||
|
raise_exception = False
|
||||||
|
) -> dict | None:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To refresh the tokens of a user. Plural 'tokens' because OAuth typically has one access token that expires
|
||||||
|
every-so-often, and one refresh token that grants you a new access token.
|
||||||
|
:param old_tokens: The current set of tokens.
|
||||||
|
:param force_refresh: To force a refresh request even if the tokens haven't yet expired.
|
||||||
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
|
:return: The same tokens if they haven't expired, refreshed tokens if the tokens have expired and were
|
||||||
|
successfully refreshed, None if the tokens have expired but could not be refreshed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# *****************************************************************************************************************
|
# *****************************************************************************************************************
|
||||||
# ***** ****
|
# ***** ****
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ import copy
|
|||||||
# To work with date and time:
|
# To work with date and time:
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
# For working with datatypes:
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
# *****************************************************************************************************************
|
# *****************************************************************************************************************
|
||||||
# ***** ****
|
# ***** ****
|
||||||
@@ -115,7 +118,7 @@ class GoogleOAuth(OAuthBase):
|
|||||||
|
|
||||||
async def initialize(
|
async def initialize(
|
||||||
self,
|
self,
|
||||||
scopes,
|
scopes: List,
|
||||||
raise_exception = False
|
raise_exception = False
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
@@ -158,7 +161,7 @@ class GoogleOAuth(OAuthBase):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
To generate an authorization URL that can be sent to the front end. When the
|
To generate an authorization URL that can be sent to the front end. When the
|
||||||
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to False, any
|
||||||
exception that occurs will be suppressed.
|
exception that occurs will be suppressed.
|
||||||
:param kwargs: Any no. of keyword args that you might want to give to this specific service.
|
:param kwargs: Any no. of keyword args that you might want to give to this specific service.
|
||||||
:return:
|
:return:
|
||||||
@@ -195,7 +198,18 @@ class GoogleOAuth(OAuthBase):
|
|||||||
self,
|
self,
|
||||||
raise_exception = False,
|
raise_exception = False,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> Credentials | dict | None:
|
) -> dict | None:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To get the tokens of a user. Plural 'tokens' because OAuth typically has one access token that expires
|
||||||
|
every-so-often, and one refresh token that grants you a new access token. In Google's case, their servers hit
|
||||||
|
your callback URL with some query params. You must furnish this whole URL right here under the kwarg
|
||||||
|
'redirect_url'. Fortunately, this URL is readily available in Quart and Flask by calling 'request.url'.
|
||||||
|
:param kwargs: The identifiers of the user who wants to use your service and any other service-specific options.
|
||||||
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
|
:return: The tokens for the service if successful, or None if failed.
|
||||||
|
"""
|
||||||
|
|
||||||
# Start by assuming failure:
|
# Start by assuming failure:
|
||||||
tokens = None
|
tokens = None
|
||||||
@@ -224,9 +238,21 @@ class GoogleOAuth(OAuthBase):
|
|||||||
async def refresh_tokens(
|
async def refresh_tokens(
|
||||||
self,
|
self,
|
||||||
old_tokens: dict,
|
old_tokens: dict,
|
||||||
|
force_refresh = False,
|
||||||
raise_exception = False
|
raise_exception = False
|
||||||
) -> dict | None:
|
) -> dict | None:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To refresh the tokens of a user. Plural 'tokens' because OAuth typically has one access token that expires
|
||||||
|
every-so-often, and one refresh token that grants you a new access token.
|
||||||
|
:param old_tokens: The current set of tokens as obtained by 'get_tokens'.
|
||||||
|
:param force_refresh: To force a refresh request even if the tokens haven't yet expired.
|
||||||
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
|
:return: The same tokens if they haven't expired, refreshed tokens if the tokens have expired and were
|
||||||
|
successfully refreshed, None if the tokens have expired but could not be refreshed.
|
||||||
|
"""
|
||||||
|
|
||||||
# Start by assuming failure:
|
# Start by assuming failure:
|
||||||
tokens = None
|
tokens = None
|
||||||
|
|
||||||
@@ -234,11 +260,11 @@ class GoogleOAuth(OAuthBase):
|
|||||||
|
|
||||||
# If the tokens haven't expired, just return the existing tokens back:
|
# If the tokens haven't expired, just return the existing tokens back:
|
||||||
tokens_expired = True if date_time.get_current_utc_date_time() >= old_tokens["expires_at"] else False
|
tokens_expired = True if date_time.get_current_utc_date_time() >= old_tokens["expires_at"] else False
|
||||||
if not tokens_expired: return old_tokens
|
if not tokens_expired and not force_refresh: return old_tokens
|
||||||
|
|
||||||
# Construct the credentials and request a refresh:
|
# Construct the credentials and request a refresh:
|
||||||
credentials = await self.credentials_from_tokens(old_tokens)
|
credentials = await self.credentials_from_tokens(old_tokens)
|
||||||
if tokens_expired and credentials.refresh_token:
|
if credentials.refresh_token:
|
||||||
credentials.refresh(Request())
|
credentials.refresh(Request())
|
||||||
tokens = {
|
tokens = {
|
||||||
"access_token": credentials.token,
|
"access_token": credentials.token,
|
||||||
@@ -271,6 +297,16 @@ class GoogleOAuth(OAuthBase):
|
|||||||
raise_exception = False
|
raise_exception = False
|
||||||
) -> Credentials | None:
|
) -> Credentials | None:
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a very service-specific thing. When using Google's APIs, you will need to use their native 'Credentials'
|
||||||
|
object. This method allows you to quickly convert the output of 'get_tokens' and 'refresh_tokens' to that object
|
||||||
|
for easy use.
|
||||||
|
:param tokens: The output of either 'get_tokens' or 'refresh_tokens'.
|
||||||
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
|
:return: Google's API's native 'Credentials' object if successful, else None.
|
||||||
|
"""
|
||||||
|
|
||||||
# Start by assuming failure:
|
# Start by assuming failure:
|
||||||
credentials = None
|
credentials = None
|
||||||
|
|
||||||
@@ -311,7 +347,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
my_goog = GoogleOAuth(
|
my_goog = GoogleOAuth(
|
||||||
config = secrets_dict,
|
config = secrets_dict,
|
||||||
redirect_url = r"https://thecaoffice.com/nexcom/converse/mail/callback/gmail",
|
redirect_url = r"https://api.thecaoffice.com/converse/mail/callback/gmail",
|
||||||
debug = True,
|
debug = True,
|
||||||
debug_prefix = "OAuth (Goog) | "
|
debug_prefix = "OAuth (Goog) | "
|
||||||
)
|
)
|
||||||
@@ -324,24 +360,24 @@ if __name__ == "__main__":
|
|||||||
user_id = "BHOPLI",
|
user_id = "BHOPLI",
|
||||||
# email = "pskhushal@gmail.com"
|
# email = "pskhushal@gmail.com"
|
||||||
))
|
))
|
||||||
|
|
||||||
redirect_url = input("Paste the redirect URL here: ")
|
redirect_url = input("Paste the redirect URL here: ")
|
||||||
print("TOKENS:", json.to_string(await my_goog.get_tokens(redirect_url = redirect_url), default = str))
|
tokens = await my_goog.get_tokens(redirect_url = redirect_url)
|
||||||
|
print("TOKENS:", json.to_string(tokens, default = str))
|
||||||
|
|
||||||
# old_tok = {
|
# # To test refreshing:
|
||||||
# "access_token": "ya29.a0AeDClZAjmLUZ1hh0aTbddz4ThjRzwQNMwdi_H4AYO-C4ETvWpK8aHYz5eV9PTUlFDJKQoHYtFgu4u2XfoiOdIEMVNwAKFbb8sakIWef7Yk5HdiDYC0A-MUp5XZnoNGLuP_GW_O3IxfCLH7cC0fb3AfHx4OsBpa_Qu1X-IpvfaCgYKAaoSARMSFQHGX2MiX81qoRex393eWE229OMCUg0175",
|
# tokens = {
|
||||||
# "refresh_token": "1//0g4mVQrydnc1ECgYIARAAGBASNgF-L9Irk_UxcRjgkz_YyK5Ujs1qCaj8nKL7bqQ0jHnHYlpVRh_Pwm77X4Angp8R-o-fgmKfzg",
|
# "access_token": "ya29.a0AeDClZAYoo85BXRId_n-hwo_amKshzi46c33GaJcsZZvGB7A7OGU2RFYcWBM_BleNBfAFUSJP2NHAvmd7Nsp_U5Kg68hXSy0iO99PNTm3pvKrJSzbkA-rXsVLsCnBIfPUMyNt2nOOVJmGwm17DNN0jAELkm1fPNTju7SZzmuaCgYKAZwSARMSFQHGX2Mis8TZui2rZT1gKySVds-N0w0175",
|
||||||
|
# "refresh_token": "1//0gnqzjMf9YT19CgYIARAAGBASNgF-L9Ir3rcY37nGrV45XyOUBRllEH7Txui7T1JbwevlmDoNw7PuMu149cCWQSwsScuKaZusUQ",
|
||||||
# "expires_in": 3539,
|
# "expires_in": 3539,
|
||||||
# "expires_at": dateparser.parse("2024-11-25 09:24:56.690876+00:00"),
|
# "expires_at": dateparser.parse("2024-11-25 10:40:40.833699+00:00"),
|
||||||
# "token_type": "Bearer",
|
|
||||||
# "scopes": [
|
# "scopes": [
|
||||||
# "https://www.googleapis.com/auth/gmail.labels",
|
# "https://www.googleapis.com/auth/gmail.labels",
|
||||||
# "https://www.googleapis.com/auth/gmail.modify"
|
# "https://www.googleapis.com/auth/gmail.modify"
|
||||||
# ]
|
# ]
|
||||||
# }
|
# }
|
||||||
#
|
|
||||||
# new_tok = await my_goog.refresh_tokens(old_tokens = old_tok)
|
new_tok = await my_goog.refresh_tokens(old_tokens = tokens, force_refresh = False)
|
||||||
# print("NEW TOKENS:", json.to_string(new_tok, default = str))
|
print("REFRESHED TOKENS:", json.to_string(new_tok, default = str))
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
Reference in New Issue
Block a user