Skip to content

Commit

Permalink
Organization api uptake changes
Browse files Browse the repository at this point in the history
  • Loading branch information
AsabuHere committed Dec 11, 2024
1 parent 76fecab commit e9eaa72
Show file tree
Hide file tree
Showing 23 changed files with 1,906 additions and 601 deletions.
3 changes: 0 additions & 3 deletions twilio/auth_strategy/auth_strategy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from twilio.auth_strategy.auth_type import AuthType
from enum import Enum
from abc import abstractmethod


Expand All @@ -14,9 +13,7 @@ def auth_type(self) -> AuthType:
@abstractmethod
def get_auth_string(self) -> str:
"""Return the authentication string."""
pass

@abstractmethod
def requires_authentication(self) -> bool:
"""Return True if authentication is required, else False."""
pass
13 changes: 7 additions & 6 deletions twilio/auth_strategy/auth_type.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from enum import Enum


class AuthType(Enum):
ORGS_TOKEN = 'orgs_stoken'
NO_AUTH = 'noauth'
BASIC = 'basic'
API_KEY = 'api_key'
CLIENT_CREDENTIALS = 'client_credentials'
ORGS_TOKEN = "orgs_stoken"
NO_AUTH = "noauth"
BASIC = "basic"
API_KEY = "api_key"
CLIENT_CREDENTIALS = "client_credentials"

def __str__(self):
return self.value
return self.value
3 changes: 2 additions & 1 deletion twilio/auth_strategy/no_auth_strategy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from auth_type import AuthType


class NoAuthStrategy(AuthStrategy):
def __init__(self):
super().__init__(AuthType.NO_AUTH)
Expand All @@ -8,4 +9,4 @@ def get_auth_string(self) -> str:
return ""

def requires_authentication(self) -> bool:
return False
return False
10 changes: 5 additions & 5 deletions twilio/auth_strategy/token_auth_strategy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import jwt
import threading
import logging
from datetime import datetime, timedelta
from datetime import datetime

from twilio.auth_strategy.auth_type import AuthType
from twilio.auth_strategy.auth_strategy import AuthStrategy
Expand All @@ -25,16 +25,16 @@ def requires_authentication(self) -> bool:
return True

def fetch_token(self):
self.logger.info("New token fetched for accessing organization API")
if self.token is None or self.token == "" or self.is_token_expired(self.token):
with self.lock:
# with self.lock:
if self.token is None or self.token == "" or self.is_token_expired(self.token):
self.logger.info("New token fetched for accessing organization API")
self.token = self.token_manager.fetch_access_token()

def is_token_expired(self, token):
try:
decoded = jwt.decode(token, options={"verify_signature": False})
exp = decoded.get('exp')
exp = decoded.get("exp")

if exp is None:
return True # No expiration time present, consider it expired
Expand All @@ -46,4 +46,4 @@ def is_token_expired(self, token):
return True # Token is invalid
except Exception as e:
print(f"An error occurred: {e}")
return True
return True
34 changes: 26 additions & 8 deletions twilio/base/client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from twilio.http import HttpClient
from twilio.http.http_client import TwilioHttpClient
from twilio.http.response import Response
from twilio.auth_strategy.auth_type import AuthType
from twilio.credential.credential_provider import CredentialProvider


Expand Down Expand Up @@ -88,27 +87,26 @@ def request(
:returns: Response from the Twilio API
"""

headers = self.get_headers(method, headers)

##If credential provider is provided by user, get the associated auth strategy
##Using the auth strategy, fetch the auth string and set it to authorization header
if self.credential_provider:

auth_strategy = self.credential_provider.to_auth_strategy()
headers["Authorization"] = auth_strategy.get_auth_string()
elif self.username is not None and self.password is not None:
auth = self.get_auth(auth)
else:
auth = None


uri = self.get_hostname(uri)

filtered_data = self.copy_non_none_values(data)
return self.http_client.request(
method,
uri,
params=params,
data=data,
data=filtered_data,
headers=headers,
auth=auth,
timeout=timeout,
Expand Down Expand Up @@ -147,7 +145,6 @@ async def request_async(
"http_client must be asynchronous to support async API requests"
)


headers = self.get_headers(method, headers)

##If credential provider is provided by user, get the associated auth strategy
Expand All @@ -162,18 +159,25 @@ async def request_async(
auth = None

uri = self.get_hostname(uri)

filtered_data = self.copy_non_none_values(data)
return await self.http_client.request(
method,
uri,
params=params,
data=data,
data=filtered_data,
headers=headers,
auth=auth,
timeout=timeout,
allow_redirects=allow_redirects,
)

def copy_non_none_values(self, data):
if isinstance(data, dict):
return {k: self.copy_non_none_values(v) for k, v in data.items() if v is not None}
elif isinstance(data, list):
return [self.copy_non_none_values(item) for item in data if item is not None]
return data

def get_auth(self, auth: Optional[Tuple[str, str]]) -> Tuple[str, str]:
"""
Get the request authentication object
Expand Down Expand Up @@ -252,6 +256,20 @@ def get_hostname(self, uri: str) -> str:
)
return str(urlunparse(parsed_url))

def remove_nulls(self, data):
res = {}
for key, sub_dict in data.items():
temp_dict = {}
if type(sub_dict) != str and sub_dict is not None:
for sub_key, sub_value in sub_dict.items():
if sub_value is not None:
temp_dict[sub_key] = sub_value
if type(sub_dict) == str:
temp_dict = sub_dict
if temp_dict:
res[key] = temp_dict
return res

def __repr__(self) -> str:
"""
Provide a friendly representation
Expand Down
2 changes: 2 additions & 0 deletions twilio/base/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ def load_page(self, payload: Dict[str, Any]):
key = keys - self.META_KEYS
if len(key) == 1:
return payload[key.pop()]
if "Resources" in payload:
return payload["Resources"]

raise TwilioException("Page Records can not be deserialized")

Expand Down
4 changes: 4 additions & 0 deletions twilio/base/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ async def fetch_async(
timeout=timeout,
allow_redirects=allow_redirects,
)
print('response')
print(response)



return self._parse_fetch(method, uri, response)

Expand Down
1 change: 1 addition & 0 deletions twilio/credential/credential_provider.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from twilio.auth_strategy.auth_type import AuthType


class CredentialProvider:
def __init__(self, auth_type: AuthType):
self._auth_type = auth_type
Expand Down
6 changes: 3 additions & 3 deletions twilio/credential/orgs_credential_provider.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


from twilio.http.orgs_token_manager import OrgTokenManager
from twilio.base.exceptions import TwilioException
from twilio.credential.credential_provider import CredentialProvider
Expand All @@ -22,7 +20,9 @@ def __init__(self, client_id: str, client_secret: str, token_manager=None):

def to_auth_strategy(self):
if self.token_manager is None:
self.token_manager = OrgTokenManager(self.grant_type, self.client_id, self.client_secret)
self.token_manager = OrgTokenManager(
self.grant_type, self.client_id, self.client_secret
)
if self.auth_strategy is None:
self.auth_strategy = TokenAuthStrategy(self.token_manager)
return self.auth_strategy
9 changes: 7 additions & 2 deletions twilio/http/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@ def request(
}
if headers and headers.get("Content-Type") == "application/json":
kwargs["json"] = data
elif headers and headers.get("Content-Type") == "application/scim+json":
kwargs["json"] = data
else:
kwargs["data"] = data
self.log_request(kwargs)
print(f'args : {kwargs}')
print(f"\nargs : {kwargs}")
self._test_only_last_response = None
session = self.session or Session()
request = Request(**kwargs)
Expand All @@ -106,9 +108,12 @@ def request(
prepped_request,
allow_redirects=allow_redirects,
timeout=timeout,
**settings
**settings,
)

print(f"\nresponse : {response.text}")
print(f"\nresponse code : {response}")

self.log_response(response.status_code, response)

self._test_only_last_response = Response(
Expand Down
2 changes: 0 additions & 2 deletions twilio/http/orgs_token_manager.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from twilio.base.version import Version
from twilio.http.token_manager import TokenManager
from twilio.rest.preview_iam.v1.token import TokenList
from twilio.rest import Client


Expand Down
10 changes: 1 addition & 9 deletions twilio/rest/preview/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from warnings import warn

from twilio.rest.preview.PreviewBase import PreviewBase
from twilio.rest.preview.deployed_devices.fleet import FleetList
from twilio.rest.preview.hosted_numbers.authorization_document import (
AuthorizationDocumentList,
)
Expand All @@ -15,14 +14,7 @@


class Preview(PreviewBase):
@property
def fleets(self) -> FleetList:
warn(
"fleets is deprecated. Use deployed_devices.fleets instead.",
DeprecationWarning,
stacklevel=2,
)
return self.deployed_devices.fleets


@property
def authorization_documents(self) -> AuthorizationDocumentList:
Expand Down
21 changes: 9 additions & 12 deletions twilio/rest/preview_iam/PreviewIamBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
from typing import Optional
from twilio.rest import Client

from twilio.rest.preview_iam.organizations import Organizations
from twilio.rest.preview_iam.v1 import V1

from twilio.rest.preview_iam.versionless import Versionless

class PreviewIamBase(Domain):
def __init__(self, twilio: Client):
Expand All @@ -25,25 +24,23 @@ def __init__(self, twilio: Client):
:returns: Domain for PreviewIam
"""
super().__init__(twilio, "https://preview-iam.twilio.com")
self._organizations: Optional[Organizations] = None
self._versionless: Optional[Versionless] = None
self._v1: Optional[V1] = None
# self._token: Optional[TokenList] = None
# self._service_accounts: Optional[ServiceAccounts] = None
# self._service_roles: Optional[ServiceRoles] = None


@property
def organizations(self) -> Organizations:
def versionless(self) -> Versionless:
"""
:returns: Organizations of PreviewIam
:returns: Versionless of PreviewIam
"""
if self._organizations is None:
self._organizations = Organizations(self)
return self._organizations
if self._versionless is None:
self._versionless = Versionless(self)
return self._versionless

@property
def v1(self) -> V1:
"""
:returns: Organizations of PreviewIam
:returns: V1 of PreviewIam
"""
if self._v1 is None:
self._v1 = V1(self)
Expand Down
40 changes: 20 additions & 20 deletions twilio/rest/preview_iam/__init__.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@

from warnings import warn
from twilio.rest.preview_iam.PreviewIamBase import PreviewIamBase
from twilio.rest.preview_iam.organizations.account import AccountList
from twilio.rest.preview_iam.organizations.role_assignment import RoleAssignmentList

# from twilio.rest.preview_iam.organizations.user import UserList
from twilio.rest.preview_iam.v1.token import TokenList
from twilio.rest.preview_iam.v1.authorize import AuthorizeList

from twilio.rest.preview_iam.versionless.organization.account import (
AccountList,
)
from twilio.rest.preview_iam.versionless.organization.role_assignment import (
RoleAssignmentList,
)
from twilio.rest.preview_iam.v1.authorize import (
AuthorizeList,
)
from twilio.rest.preview_iam.v1.token import (
TokenList,
)
from twilio.rest.preview_iam.versionless.organization import (
OrganizationList,
)

class PreviewIam(PreviewIamBase):

@property
def accounts(self) -> AccountList:
return self.organizations.accounts
def organization(self) -> OrganizationList:
return self.versionless.organization

@property
def role_assignments(self) -> RoleAssignmentList:
return self.organizations.role_assignments

# @property
# def users(self) -> UserList:
# return self.organizations.users
@property
def authorize(self) -> AuthorizeList:
return self.v1.authorize

@property
def token(self) -> TokenList:
return self.v1.token

@property
def authorize(self) -> AuthorizeList:
return self.v1.authorize
Loading

0 comments on commit e9eaa72

Please sign in to comment.