From b4c5734d8bf364600d4866405a622a09076e21f8 Mon Sep 17 00:00:00 2001 From: Manisha Singh Date: Tue, 23 Jul 2024 15:42:00 +0530 Subject: [PATCH 01/33] feat: oauth sdk implementation (#799) * chore: oauth sdk implementation --- twilio/base/client_base.py | 25 ++- twilio/base/domain.py | 5 + twilio/base/oauth_token_base.py | 24 +++ twilio/base/version.py | 19 +++ twilio/http/bearer_token_http_client.py | 30 ++++ twilio/http/http_client.py | 1 + twilio/http/no_auth_http_client.py | 4 + twilio/http/orgs_token_manager.py | 42 +++++ twilio/http/token_manager.py | 7 + twilio/http/token_manager_initializer.py | 16 ++ twilio/rest/__init__.py | 14 ++ twilio/rest/preview_iam/PreviewIamBase.py | 29 ++++ .../rest/preview_iam/organizations/token.py | 160 ++++++++++++++++++ twilio/twilio_bearer_token_auth.py | 33 ++++ 14 files changed, 406 insertions(+), 3 deletions(-) create mode 100644 twilio/base/oauth_token_base.py create mode 100644 twilio/http/bearer_token_http_client.py create mode 100644 twilio/http/no_auth_http_client.py create mode 100644 twilio/http/orgs_token_manager.py create mode 100644 twilio/http/token_manager.py create mode 100644 twilio/http/token_manager_initializer.py create mode 100644 twilio/rest/preview_iam/PreviewIamBase.py create mode 100644 twilio/rest/preview_iam/organizations/token.py create mode 100644 twilio/twilio_bearer_token_auth.py diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 5f17c7540..700e420ad 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -69,6 +69,8 @@ def request( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, + domain: Optional[str] = None ) -> Response: """ Makes a request to the Twilio API using the configured http client @@ -85,9 +87,15 @@ def request( :returns: Response from the Twilio API """ - auth = self.get_auth(auth) + if not is_oauth: + auth = self.get_auth(auth) headers = self.get_headers(method, headers) uri = self.get_hostname(uri) + if is_oauth: + OauthTokenBase = dynamic_import("twilio.base.oauth_token_base", "OauthTokenBase") + token = OauthTokenBase().get_oauth_token(domain, "v1", self.username, self.password) + headers['Authorization'] = f'Bearer {token}' + headers.get('Authorization') return self.http_client.request( method, @@ -110,6 +118,7 @@ async def request_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Response: """ Asynchronously makes a request to the Twilio API using the configured http client @@ -131,10 +140,15 @@ async def request_async( raise RuntimeError( "http_client must be asynchronous to support async API requests" ) - - auth = self.get_auth(auth) + if not is_oauth: + auth = self.get_auth(auth) headers = self.get_headers(method, headers) uri = self.get_hostname(uri) + if is_oauth: + OauthTokenBase = dynamic_import("twilio.base.oauth_token_base", "OauthTokenBase") + token = OauthTokenBase().get_oauth_token(domain, "v1", self.username, self.password) + headers['Authorization'] = f'Bearer {token}' + headers.get('Authorization') return await self.http_client.request( method, @@ -232,3 +246,8 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ return "".format(self.account_sid) + +def dynamic_import(module_name, class_name): + from importlib import import_module + module = import_module(module_name) + return getattr(module, class_name) diff --git a/twilio/base/domain.py b/twilio/base/domain.py index 4f8395ddf..02f498685 100644 --- a/twilio/base/domain.py +++ b/twilio/base/domain.py @@ -32,6 +32,7 @@ def request( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Response: """ Makes an HTTP request to this domain. @@ -55,6 +56,8 @@ def request( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth, + domain=self.base_url, ) async def request_async( @@ -67,6 +70,7 @@ async def request_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Response: """ Makes an asynchronous HTTP request to this domain. @@ -90,4 +94,5 @@ async def request_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) diff --git a/twilio/base/oauth_token_base.py b/twilio/base/oauth_token_base.py new file mode 100644 index 000000000..91e6d5191 --- /dev/null +++ b/twilio/base/oauth_token_base.py @@ -0,0 +1,24 @@ +from twilio.http.token_manager_initializer import TokenManagerInitializer + +# Dynamic import utility function +def dynamic_import(module_name, class_name): + from importlib import import_module + module = import_module(module_name) + return getattr(module, class_name) + +class OauthTokenBase: + def get_oauth_token(self, domain: str, version: str, username: str, password: str): + Domain = dynamic_import("twilio.base.domain", "Domain") + Version = dynamic_import("twilio.base.version", "Version") + BearerTokenHTTPClient = dynamic_import("twilio.http.bearer_token_http_client", "BearerTokenHTTPClient") + OrgTokenManager = dynamic_import("twilio.http.orgs_token_manager", "OrgTokenManager") + Client = dynamic_import("twilio.rest", "Client") + try: + orgs_token_manager = TokenManagerInitializer.get_token_manager() + return BearerTokenHTTPClient(orgs_token_manager).get_access_token(Version(Domain(Client(username, password), domain), version)) + except Exception: + orgs_token_manager = OrgTokenManager(grant_type='client_credentials', + client_id=username, + client_secret=password) + TokenManagerInitializer().set_token_manager(orgs_token_manager) + return BearerTokenHTTPClient(orgs_token_manager).get_access_token(Version(Domain(Client(username, password), domain), version)) \ No newline at end of file diff --git a/twilio/base/version.py b/twilio/base/version.py index 64cc601fa..e7ddaff6c 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -39,6 +39,7 @@ def request( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Response: """ Make an HTTP request. @@ -53,6 +54,7 @@ def request( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) async def request_async( @@ -65,6 +67,7 @@ async def request_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Response: """ Make an asynchronous HTTP request @@ -79,6 +82,7 @@ async def request_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) @classmethod @@ -123,6 +127,7 @@ def fetch( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Any: """ Fetch a resource instance. @@ -136,6 +141,7 @@ def fetch( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) return self._parse_fetch(method, uri, response) @@ -150,6 +156,7 @@ async def fetch_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Any: """ Asynchronously fetch a resource instance. @@ -163,6 +170,7 @@ async def fetch_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) return self._parse_fetch(method, uri, response) @@ -186,6 +194,7 @@ def update( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Any: """ Update a resource instance. @@ -213,6 +222,7 @@ async def update_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Any: """ Asynchronously update a resource instance. @@ -226,6 +236,7 @@ async def update_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) return self._parse_update(method, uri, response) @@ -249,6 +260,7 @@ def delete( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> bool: """ Delete a resource. @@ -276,6 +288,7 @@ async def delete_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> bool: """ Asynchronously delete a resource. @@ -289,6 +302,7 @@ async def delete_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) return self._parse_delete(method, uri, response) @@ -347,6 +361,7 @@ async def page_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Response: """ Makes an asynchronous HTTP request. @@ -360,6 +375,7 @@ async def page_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) def stream( @@ -447,6 +463,7 @@ def create( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Any: """ Create a resource instance. @@ -474,6 +491,7 @@ async def create_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Any: """ Asynchronously create a resource instance. @@ -487,6 +505,7 @@ async def create_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, + is_oauth=is_oauth ) return self._parse_create(method, uri, response) diff --git a/twilio/http/bearer_token_http_client.py b/twilio/http/bearer_token_http_client.py new file mode 100644 index 000000000..eb5758db9 --- /dev/null +++ b/twilio/http/bearer_token_http_client.py @@ -0,0 +1,30 @@ +import datetime +import jwt + +from twilio.base.version import Version +from twilio.http.token_manager import TokenManager +from twilio.twilio_bearer_token_auth import TwilioBearerTokenAuth + + +class BearerTokenHTTPClient: + def __init__(self, orgs_token_manager: TokenManager): + self.orgs_token_manager = orgs_token_manager + + def get_access_token(self, version: Version): + if TwilioBearerTokenAuth.get_access_token() is None or self.is_token_expired( + TwilioBearerTokenAuth.get_access_token() + ): + access_token = self.orgs_token_manager.fetch_access_token(version) + TwilioBearerTokenAuth.init(access_token) + else: + access_token = TwilioBearerTokenAuth.get_access_token() + + return access_token + + def is_token_expired(self, token): + decoded_jwt = jwt.decode(token, options={"verify_signature": True}) + expires_at = decoded_jwt.get("exp") + # Add a buffer of 30 seconds + buffer_seconds = 30 + buffer_expires_at = expires_at - buffer_seconds + return buffer_expires_at < datetime.datetime.now().timestamp() diff --git a/twilio/http/http_client.py b/twilio/http/http_client.py index 9d329c6aa..c97c6e830 100644 --- a/twilio/http/http_client.py +++ b/twilio/http/http_client.py @@ -57,6 +57,7 @@ def request( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, + is_oauth: bool = False, ) -> Response: """ Make an HTTP Request with parameters provided. diff --git a/twilio/http/no_auth_http_client.py b/twilio/http/no_auth_http_client.py new file mode 100644 index 000000000..daa5e5cdc --- /dev/null +++ b/twilio/http/no_auth_http_client.py @@ -0,0 +1,4 @@ +class NoAuthHTTPClient: + def get_headers(self): + headers = {} + return headers diff --git a/twilio/http/orgs_token_manager.py b/twilio/http/orgs_token_manager.py new file mode 100644 index 000000000..fbde521a3 --- /dev/null +++ b/twilio/http/orgs_token_manager.py @@ -0,0 +1,42 @@ +from twilio.base.version import Version +from twilio.http.token_manager import TokenManager +from twilio.rest.preview_iam.organizations.token import TokenList + + +class OrgTokenManager(TokenManager): + """ + Orgs Token Manager + """ + + def __init__( + self, + grant_type: str, + client_id: str, + client_secret: str, + code: str = None, + redirect_uri: str = None, + audience: str = None, + refreshToken: str = None, + scope: str = None, + ): + self.grant_type = grant_type + self.client_id = client_id + self.client_secret = client_secret + self.code = code + self.redirect_uri = redirect_uri + self.audience = audience + self.refreshToken = refreshToken + self.scope = scope + + def fetch_access_token(self, version: Version): + token_list = TokenList(version) + token_instance = token_list.create( + grant_type=self.grant_type, + client_id=self.client_id, + client_secret=self.client_secret, + code=self.code, + redirect_uri=self.redirect_uri, + audience=self.audience, + scope=self.scope, + ) + return token_instance.access_token diff --git a/twilio/http/token_manager.py b/twilio/http/token_manager.py new file mode 100644 index 000000000..28cc73101 --- /dev/null +++ b/twilio/http/token_manager.py @@ -0,0 +1,7 @@ +from twilio.base.version import Version + + +class TokenManager: + + def fetch_access_token(self, version: Version): + pass diff --git a/twilio/http/token_manager_initializer.py b/twilio/http/token_manager_initializer.py new file mode 100644 index 000000000..d4836d68a --- /dev/null +++ b/twilio/http/token_manager_initializer.py @@ -0,0 +1,16 @@ +from twilio.http.token_manager import TokenManager + + +class TokenManagerInitializer: + + org_token_manager = None + + @classmethod + def set_token_manager(cls, token_manager: TokenManager): + cls.org_token_manager = token_manager + + @classmethod + def get_token_manager(cls): + if cls.org_token_manager is None: + raise Exception('Token Manager not initialized') + return cls.org_token_manager \ No newline at end of file diff --git a/twilio/rest/__init__.py b/twilio/rest/__init__.py index 31d114a6c..6da8da4a1 100644 --- a/twilio/rest/__init__.py +++ b/twilio/rest/__init__.py @@ -145,6 +145,7 @@ def __init__( self._numbers: Optional["Numbers"] = None self._oauth: Optional["Oauth"] = None self._preview: Optional["Preview"] = None + self._preview_iam: Optional["PreviewIam"] = None self._pricing: Optional["Pricing"] = None self._proxy: Optional["Proxy"] = None self._routes: Optional["Routes"] = None @@ -446,6 +447,19 @@ def preview(self) -> "Preview": self._preview = Preview(self) return self._preview + @property + def preview_iam(self) -> "PreviewIam": + """ + Access the Preview Twilio Domain + + :returns: Preview Twilio Domain + """ + if self._preview_iam is None: + from twilio.rest.preview_iam import PreviewIam + + self._preview = PreviewIam(self) + return self._preview_iam + @property def pricing(self) -> "Pricing": """ diff --git a/twilio/rest/preview_iam/PreviewIamBase.py b/twilio/rest/preview_iam/PreviewIamBase.py new file mode 100644 index 000000000..9cf1f3bac --- /dev/null +++ b/twilio/rest/preview_iam/PreviewIamBase.py @@ -0,0 +1,29 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.preview_iam.organizations_openapi.token import Token +from twilio.rest.preview_iam.organizations_openapi.account import Account +from twilio.rest.preview_iam.organizations_openapi.authorize import AuthorizeList +from twilio.rest.preview_iam.organizations_openapi.resource_type import ResourceTypeList +from twilio.rest.preview_iam.organizations_openapi.role_assignment import RoleAssignmentList + +class PreviewIamBase(Domain): + def __init__(self, twilio: Client): + """ + Initialize the PreviewIam Domain + + :returns: Domain for PreviewIam + """ + super().__init__(twilio, "https://preview.twilio.com/iam") + self._token: Optional[TokenList] = None + self._service_accounts: Optional[ServiceAccounts] = None + self._service_roles: Optional[ServiceRoles] = None \ No newline at end of file diff --git a/twilio/rest/preview_iam/organizations/token.py b/twilio/rest/preview_iam/organizations/token.py new file mode 100644 index 000000000..fc28287e8 --- /dev/null +++ b/twilio/rest/preview_iam/organizations/token.py @@ -0,0 +1,160 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Organization Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + + +from datetime import date, datetime +from decimal import Decimal +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + + +class TokenInstance(InstanceResource): + + """ + :ivar access_token: Token which carries the necessary information to access a Twilio resource directly. + :ivar refresh_token: Token which carries the information necessary to get a new access token. + :ivar id_token: Token which carries the information necessary of user profile. + :ivar token_type: Token type + :ivar expires_in: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + + self.access_token: Optional[str] = payload.get("access_token") + self.refresh_token: Optional[str] = payload.get("refresh_token") + self.id_token: Optional[str] = payload.get("id_token") + self.token_type: Optional[str] = payload.get("token_type") + self.expires_in: Optional[int] = payload.get("expires_in") + + + + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return '' + + + + +class TokenList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the TokenList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + + self._uri = '/token' + + + + def create(self, grant_type: str, client_id: str, client_secret: Union[str, object]=values.unset, code: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, audience: Union[str, object]=values.unset, refresh_token: Union[str, object]=values.unset, scope: Union[str, object]=values.unset) -> TokenInstance: + """ + Create the TokenInstance + + :param grant_type: Grant type is a credential representing resource owner's authorization which can be used by client to obtain access token. + :param client_id: A 34 character string that uniquely identifies this OAuth App. + :param client_secret: The credential for confidential OAuth App. + :param code: JWT token related to the authorization code grant type. + :param redirect_uri: The redirect uri + :param audience: The targeted audience uri + :param refresh_token: JWT token related to refresh access token. + :param scope: The scope of token + + :returns: The created TokenInstance + """ + + data = values.of({ + 'grant_type': grant_type, + 'client_id': client_id, + 'client_secret': client_secret, + 'code': code, + 'redirect_uri': redirect_uri, + 'audience': audience, + 'refresh_token': refresh_token, + 'scope': scope, + }) + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + + payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) + + return TokenInstance(self._version, payload) + + async def create_async(self, grant_type: str, client_id: str, client_secret: Union[str, object]=values.unset, code: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, audience: Union[str, object]=values.unset, refresh_token: Union[str, object]=values.unset, scope: Union[str, object]=values.unset) -> TokenInstance: + """ + Asynchronously create the TokenInstance + + :param grant_type: Grant type is a credential representing resource owner's authorization which can be used by client to obtain access token. + :param client_id: A 34 character string that uniquely identifies this OAuth App. + :param client_secret: The credential for confidential OAuth App. + :param code: JWT token related to the authorization code grant type. + :param redirect_uri: The redirect uri + :param audience: The targeted audience uri + :param refresh_token: JWT token related to refresh access token. + :param scope: The scope of token + + :returns: The created TokenInstance + """ + + data = values.of({ + 'grant_type': grant_type, + 'client_id': client_id, + 'client_secret': client_secret, + 'code': code, + 'redirect_uri': redirect_uri, + 'audience': audience, + 'refresh_token': refresh_token, + 'scope': scope, + }) + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + + payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) + + return TokenInstance(self._version, payload) + + + + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return '' + diff --git a/twilio/twilio_bearer_token_auth.py b/twilio/twilio_bearer_token_auth.py new file mode 100644 index 000000000..ffc8814cd --- /dev/null +++ b/twilio/twilio_bearer_token_auth.py @@ -0,0 +1,33 @@ +from threading import Lock + + +class BearerTokenTwilioRestClient: + pass + + +class TwilioBearerTokenAuth: + _lock = Lock() + access_token = None + rest_client = None + user_agent_extensions = None + region = None + edge = None + + @classmethod + def init(cls, access_token): + with cls._lock: + if not access_token: + raise ValueError("Access Token cannot be null or Empty") + if access_token != cls.access_token: + cls.access_token = None + cls.access_token = access_token + + @classmethod + def get_access_token(cls): + with cls._lock: + return cls.access_token + + @classmethod + def get_header_param(cls): + with cls._lock: + return {"Authorization": "Bearer {token}".format(token=cls.access_token)} From 3e246e452b668024609a995af44b02922f79145a Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Tue, 17 Sep 2024 16:26:15 +0530 Subject: [PATCH 02/33] Python Orgs Api Changes --- tests/cluster/test_cluster.py | 9 + tests/cluster/test_webhook.py | 156 +++--- twilio/base/client_base.py | 30 +- twilio/base/domain.py | 2 +- twilio/base/exceptions.py | 3 +- twilio/base/oauth_token_base.py | 27 +- twilio/base/version.py | 24 +- twilio/http/token_manager_initializer.py | 4 +- twilio/rest/__init__.py | 14 - twilio/rest/preview_iam/PreviewIamBase.py | 37 +- twilio/rest/preview_iam/__init__.py | 57 ++ .../preview_iam/organizations/__init__.py | 58 ++ .../rest/preview_iam/organizations/account.py | 422 ++++++++++++++ .../organizations/role_assignment.py | 525 ++++++++++++++++++ twilio/rest/preview_iam/v1/__init__.py | 51 ++ twilio/rest/preview_iam/v1/authorize.py | 126 +++++ .../{organizations => v1}/token.py | 140 ++--- 17 files changed, 1488 insertions(+), 197 deletions(-) create mode 100644 twilio/rest/preview_iam/__init__.py create mode 100644 twilio/rest/preview_iam/organizations/__init__.py create mode 100644 twilio/rest/preview_iam/organizations/account.py create mode 100644 twilio/rest/preview_iam/organizations/role_assignment.py create mode 100644 twilio/rest/preview_iam/v1/__init__.py create mode 100644 twilio/rest/preview_iam/v1/authorize.py rename twilio/rest/preview_iam/{organizations => v1}/token.py (59%) diff --git a/tests/cluster/test_cluster.py b/tests/cluster/test_cluster.py index 556fa8da7..76a6f85d0 100644 --- a/tests/cluster/test_cluster.py +++ b/tests/cluster/test_cluster.py @@ -19,6 +19,15 @@ def setUp(self): ) self.voice_twiml = VoiceResponse() + + def test_token_fetch(self): + token = self.client.preview_iam.token.create( + grant_type = GRANT_TYPE, + client_id = CLIENT_ID, + client_secret = CLIENT_SECRET) + print(f'{token}') + + def test_send_text_message(self): msg = self.client.messages.create( to=self.to_number, from_=self.from_number, body="hello world" diff --git a/tests/cluster/test_webhook.py b/tests/cluster/test_webhook.py index 307cf3d39..7cf8fbac8 100644 --- a/tests/cluster/test_webhook.py +++ b/tests/cluster/test_webhook.py @@ -29,81 +29,81 @@ def process_request(self): ) -# class WebhookTest(unittest.TestCase): -# def setUp(self): -# api_key = os.environ["TWILIO_API_KEY"] -# api_secret = os.environ["TWILIO_API_SECRET"] -# account_sid = os.environ["TWILIO_ACCOUNT_SID"] -# self.client = Client(api_key, api_secret, account_sid) -# -# portNumber = 7777 -# self.validation_server = HTTPServer(("", portNumber), RequestHandler) -# self.tunnel = ngrok.connect(portNumber) -# self.flow_sid = "" -# _thread.start_new_thread(self.start_http_server, ()) -# -# def start_http_server(self): -# self.validation_server.serve_forever() -# -# def tearDown(self): -# self.client.studio.v2.flows(self.flow_sid).delete() -# ngrok.kill() -# self.validation_server.shutdown() -# self.validation_server.server_close() -# -# def create_studio_flow(self, url, method): -# flow = self.client.studio.v2.flows.create( -# friendly_name="Python Cluster Test Flow", -# status="published", -# definition={ -# "description": "Studio Flow", -# "states": [ -# { -# "name": "Trigger", -# "type": "trigger", -# "transitions": [ -# { -# "next": "httpRequest", -# "event": "incomingRequest", -# }, -# ], -# "properties": {}, -# }, -# { -# "name": "httpRequest", -# "type": "make-http-request", -# "transitions": [], -# "properties": { -# "method": method, -# "content_type": "application/x-www-form-urlencoded;charset=utf-8", -# "url": url, -# }, -# }, -# ], -# "initial_state": "Trigger", -# "flags": { -# "allow_concurrent_calls": True, -# }, -# }, -# ) -# return flow -# -# def validate(self, method): -# flow = self.create_studio_flow(url=self.tunnel.public_url, method=method) -# self.flow_sid = flow.sid -# time.sleep(5) -# self.client.studio.v2.flows(self.flow_sid).executions.create( -# to="to", from_="from" -# ) -# -# def test_get(self): -# time.sleep(5) -# self.validate("GET") -# time.sleep(5) -# self.assertEqual(RequestHandler.is_request_valid, True) -# -# def test_post(self): -# time.sleep(5) -# self.validate("POST") -# time.sleep(5) -# self.assertEqual(RequestHandler.is_request_valid, True) +class WebhookTest(unittest.TestCase): + def setUp(self): + api_key = os.environ["TWILIO_API_KEY"] + api_secret = os.environ["TWILIO_API_SECRET"] + account_sid = os.environ["TWILIO_ACCOUNT_SID"] + self.client = Client(api_key, api_secret, account_sid) + + portNumber = 7777 + self.validation_server = HTTPServer(("", portNumber), RequestHandler) + self.tunnel = ngrok.connect(portNumber) + self.flow_sid = "" + _thread.start_new_thread(self.start_http_server, ()) + + def start_http_server(self): + self.validation_server.serve_forever() + + def tearDown(self): + self.client.studio.v2.flows(self.flow_sid).delete() + ngrok.kill() + self.validation_server.shutdown() + self.validation_server.server_close() + + def create_studio_flow(self, url, method): + flow = self.client.studio.v2.flows.create( + friendly_name="Python Cluster Test Flow", + status="published", + definition={ + "description": "Studio Flow", + "states": [ + { + "name": "Trigger", + "type": "trigger", + "transitions": [ + { + "next": "httpRequest", + "event": "incomingRequest", + }, + ], + "properties": {}, + }, + { + "name": "httpRequest", + "type": "make-http-request", + "transitions": [], + "properties": { + "method": method, + "content_type": "application/x-www-form-urlencoded;charset=utf-8", + "url": url, + }, + }, + ], + "initial_state": "Trigger", + "flags": { + "allow_concurrent_calls": True, + }, + }, + ) + return flow + + def validate(self, method): + flow = self.create_studio_flow(url=self.tunnel.public_url, method=method) + self.flow_sid = flow.sid + time.sleep(5) + self.client.studio.v2.flows(self.flow_sid).executions.create( + to="to", from_="from" + ) + + def test_get(self): + time.sleep(5) + self.validate("GET") + time.sleep(5) + self.assertEqual(RequestHandler.is_request_valid, True) + + def test_post(self): + time.sleep(5) + self.validate("POST") + time.sleep(5) + self.assertEqual(RequestHandler.is_request_valid, True) diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 700e420ad..1bee0f613 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -70,7 +70,7 @@ def request( timeout: Optional[float] = None, allow_redirects: bool = False, is_oauth: bool = False, - domain: Optional[str] = None + domain: Optional[str] = None, ) -> Response: """ Makes a request to the Twilio API using the configured http client @@ -87,15 +87,21 @@ def request( :returns: Response from the Twilio API """ + + print('*****') if not is_oauth: auth = self.get_auth(auth) headers = self.get_headers(method, headers) uri = self.get_hostname(uri) if is_oauth: - OauthTokenBase = dynamic_import("twilio.base.oauth_token_base", "OauthTokenBase") - token = OauthTokenBase().get_oauth_token(domain, "v1", self.username, self.password) - headers['Authorization'] = f'Bearer {token}' - headers.get('Authorization') + OauthTokenBase = dynamic_import( + "twilio.base.oauth_token_base", "OauthTokenBase" + ) + token = OauthTokenBase().get_oauth_token( + domain, "v1", self.username, self.password + ) + headers["Authorization"] = f"Bearer {token}" + headers.get("Authorization") return self.http_client.request( method, @@ -145,10 +151,14 @@ async def request_async( headers = self.get_headers(method, headers) uri = self.get_hostname(uri) if is_oauth: - OauthTokenBase = dynamic_import("twilio.base.oauth_token_base", "OauthTokenBase") - token = OauthTokenBase().get_oauth_token(domain, "v1", self.username, self.password) - headers['Authorization'] = f'Bearer {token}' - headers.get('Authorization') + OauthTokenBase = dynamic_import( + "twilio.base.oauth_token_base", "OauthTokenBase" + ) + token = OauthTokenBase().get_oauth_token( + domain, "v1", self.username, self.password + ) + headers["Authorization"] = f"Bearer {token}" + headers.get("Authorization") return await self.http_client.request( method, @@ -247,7 +257,9 @@ def __repr__(self) -> str: """ return "".format(self.account_sid) + def dynamic_import(module_name, class_name): from importlib import import_module + module = import_module(module_name) return getattr(module, class_name) diff --git a/twilio/base/domain.py b/twilio/base/domain.py index 02f498685..72724aa9d 100644 --- a/twilio/base/domain.py +++ b/twilio/base/domain.py @@ -94,5 +94,5 @@ async def request_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) diff --git a/twilio/base/exceptions.py b/twilio/base/exceptions.py index 8f3b7cc7a..05a7c96e8 100644 --- a/twilio/base/exceptions.py +++ b/twilio/base/exceptions.py @@ -62,7 +62,8 @@ def get_uri(code: int) -> str: "\n\n{twilio_returned}\n\n{message}\n".format( red_error=red("HTTP Error"), request_was=white("Your request was:"), - http_line=teal("%s %s" % (self.method, self.uri)), + http_line=teal("%s %s" % (self.method, self.uri, self.data, self.uri)), + http_line=teal("%s %s" % (self.data, self.headers)), twilio_returned=white("Twilio returned the following information:"), message=blue(str(self.msg)), ) diff --git a/twilio/base/oauth_token_base.py b/twilio/base/oauth_token_base.py index 91e6d5191..f58a143c8 100644 --- a/twilio/base/oauth_token_base.py +++ b/twilio/base/oauth_token_base.py @@ -1,24 +1,37 @@ from twilio.http.token_manager_initializer import TokenManagerInitializer + # Dynamic import utility function def dynamic_import(module_name, class_name): from importlib import import_module + module = import_module(module_name) return getattr(module, class_name) + class OauthTokenBase: def get_oauth_token(self, domain: str, version: str, username: str, password: str): Domain = dynamic_import("twilio.base.domain", "Domain") Version = dynamic_import("twilio.base.version", "Version") - BearerTokenHTTPClient = dynamic_import("twilio.http.bearer_token_http_client", "BearerTokenHTTPClient") - OrgTokenManager = dynamic_import("twilio.http.orgs_token_manager", "OrgTokenManager") + BearerTokenHTTPClient = dynamic_import( + "twilio.http.bearer_token_http_client", "BearerTokenHTTPClient" + ) + OrgTokenManager = dynamic_import( + "twilio.http.orgs_token_manager", "OrgTokenManager" + ) Client = dynamic_import("twilio.rest", "Client") try: orgs_token_manager = TokenManagerInitializer.get_token_manager() - return BearerTokenHTTPClient(orgs_token_manager).get_access_token(Version(Domain(Client(username, password), domain), version)) + return BearerTokenHTTPClient(orgs_token_manager).get_access_token( + Version(Domain(Client(username, password), domain), version) + ) except Exception: - orgs_token_manager = OrgTokenManager(grant_type='client_credentials', - client_id=username, - client_secret=password) + orgs_token_manager = OrgTokenManager( + grant_type="client_credentials", + client_id=username, + client_secret=password, + ) TokenManagerInitializer().set_token_manager(orgs_token_manager) - return BearerTokenHTTPClient(orgs_token_manager).get_access_token(Version(Domain(Client(username, password), domain), version)) \ No newline at end of file + return BearerTokenHTTPClient(orgs_token_manager).get_access_token( + Version(Domain(Client(username, password), domain), version) + ) diff --git a/twilio/base/version.py b/twilio/base/version.py index e7ddaff6c..58fc809af 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -44,6 +44,7 @@ def request( """ Make an HTTP request. """ + print('88888') url = self.relative_uri(uri) return self.domain.request( method, @@ -54,7 +55,7 @@ def request( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) async def request_async( @@ -82,7 +83,7 @@ async def request_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) @classmethod @@ -141,7 +142,7 @@ def fetch( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) return self._parse_fetch(method, uri, response) @@ -170,7 +171,7 @@ async def fetch_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) return self._parse_fetch(method, uri, response) @@ -236,7 +237,7 @@ async def update_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) return self._parse_update(method, uri, response) @@ -302,7 +303,7 @@ async def delete_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) return self._parse_delete(method, uri, response) @@ -375,7 +376,7 @@ async def page_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) def stream( @@ -448,6 +449,7 @@ def _parse_create(self, method: str, uri: str, response: Response) -> Any: """ Parse create response JSON """ + print('99999') if response.status_code < 200 or response.status_code >= 300: raise self.exception(method, uri, response, "Unable to create record") @@ -468,6 +470,7 @@ def create( """ Create a resource instance. """ + print('******') response = self.request( method, uri, @@ -478,7 +481,7 @@ def create( timeout=timeout, allow_redirects=allow_redirects, ) - + print('******') return self._parse_create(method, uri, response) async def create_async( @@ -496,6 +499,7 @@ async def create_async( """ Asynchronously create a resource instance. """ + print('******') response = await self.request_async( method, uri, @@ -505,7 +509,7 @@ async def create_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth + is_oauth=is_oauth, ) - + print('******') return self._parse_create(method, uri, response) diff --git a/twilio/http/token_manager_initializer.py b/twilio/http/token_manager_initializer.py index d4836d68a..36ee83f1a 100644 --- a/twilio/http/token_manager_initializer.py +++ b/twilio/http/token_manager_initializer.py @@ -12,5 +12,5 @@ def set_token_manager(cls, token_manager: TokenManager): @classmethod def get_token_manager(cls): if cls.org_token_manager is None: - raise Exception('Token Manager not initialized') - return cls.org_token_manager \ No newline at end of file + raise Exception("Token Manager not initialized") + return cls.org_token_manager diff --git a/twilio/rest/__init__.py b/twilio/rest/__init__.py index 6da8da4a1..9c71ce2bb 100644 --- a/twilio/rest/__init__.py +++ b/twilio/rest/__init__.py @@ -132,7 +132,6 @@ def __init__( self._events: Optional["Events"] = None self._flex_api: Optional["FlexApi"] = None self._frontline_api: Optional["FrontlineApi"] = None - self._preview_iam: Optional["PreviewIam"] = None self._insights: Optional["Insights"] = None self._intelligence: Optional["Intelligence"] = None self._ip_messaging: Optional["IpMessaging"] = None @@ -447,19 +446,6 @@ def preview(self) -> "Preview": self._preview = Preview(self) return self._preview - @property - def preview_iam(self) -> "PreviewIam": - """ - Access the Preview Twilio Domain - - :returns: Preview Twilio Domain - """ - if self._preview_iam is None: - from twilio.rest.preview_iam import PreviewIam - - self._preview = PreviewIam(self) - return self._preview_iam - @property def pricing(self) -> "Pricing": """ diff --git a/twilio/rest/preview_iam/PreviewIamBase.py b/twilio/rest/preview_iam/PreviewIamBase.py index 9cf1f3bac..bcabdda5b 100644 --- a/twilio/rest/preview_iam/PreviewIamBase.py +++ b/twilio/rest/preview_iam/PreviewIamBase.py @@ -8,13 +8,14 @@ https://openapi-generator.tech Do not edit the class manually. """ + from twilio.base.domain import Domain +from typing import Optional from twilio.rest import Client -from twilio.rest.preview_iam.organizations_openapi.token import Token -from twilio.rest.preview_iam.organizations_openapi.account import Account -from twilio.rest.preview_iam.organizations_openapi.authorize import AuthorizeList -from twilio.rest.preview_iam.organizations_openapi.resource_type import ResourceTypeList -from twilio.rest.preview_iam.organizations_openapi.role_assignment import RoleAssignmentList + +from twilio.rest.preview_iam.organizations import Organizations +from twilio.rest.preview_iam.v1 import V1 + class PreviewIamBase(Domain): def __init__(self, twilio: Client): @@ -24,6 +25,26 @@ def __init__(self, twilio: Client): :returns: Domain for PreviewIam """ super().__init__(twilio, "https://preview.twilio.com/iam") - self._token: Optional[TokenList] = None - self._service_accounts: Optional[ServiceAccounts] = None - self._service_roles: Optional[ServiceRoles] = None \ No newline at end of file + self._organizations: Optional[Organizations] = 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: + """ + :returns: Organizations of PreviewIam + """ + if self._organizations is None: + self._organizations = Organizations(self) + return self._organizations + + @property + def v1(self) -> V1: + """ + :returns: Organizations of PreviewIam + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 diff --git a/twilio/rest/preview_iam/__init__.py b/twilio/rest/preview_iam/__init__.py new file mode 100644 index 000000000..f4f47f863 --- /dev/null +++ b/twilio/rest/preview_iam/__init__.py @@ -0,0 +1,57 @@ + +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 + + +class PreviewIam(PreviewIamBase): + + @property + def accounts(self) -> AccountList: + warn( + "accounts is deprecated. Use organizations.accounts instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.organizations.accounts + + @property + def role_assignments(self) -> RoleAssignmentList: + warn( + "role_assignments is deprecated. Use organizations.role_assignments instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.organizations.role_assignments + + # @property + # def users(self) -> UserList: + # warn( + # "users is deprecated. Use organizations.users instead.", + # DeprecationWarning, + # stacklevel=2, + # ) + # return self.organizations.users + + @property + def token(self) -> TokenList: + warn( + "token is deprecated. Use v1.token instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.token + + @property + def authorize(self) -> AuthorizeList: + warn( + "authorize is deprecated. Use v1.authorize instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.authorize diff --git a/twilio/rest/preview_iam/organizations/__init__.py b/twilio/rest/preview_iam/organizations/__init__.py new file mode 100644 index 000000000..73b726d2e --- /dev/null +++ b/twilio/rest/preview_iam/organizations/__init__.py @@ -0,0 +1,58 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Organization Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview_iam.organizations.account import AccountList +from twilio.rest.preview_iam.organizations.role_assignment import RoleAssignmentList + + +class Organizations(Version): + + def __init__(self, domain: Domain): + """ + Initialize the Organizations version of PreviewIam + + :param domain: The Twilio.preview_iam domain + """ + super().__init__(domain, "Organizations") + self._accounts: Optional[AccountList] = None + self._role_assignments: Optional[RoleAssignmentList] = None + self._users: Optional[UserList] = None + + @property + def accounts(self) -> AccountList: + if self._accounts is None: + self._accounts = AccountList(self) + return self._accounts + + @property + def role_assignments(self) -> RoleAssignmentList: + if self._role_assignments is None: + self._role_assignments = RoleAssignmentList(self) + return self._role_assignments + + # @property + # def users(self) -> UserList: + # if self._users is None: + # self._users = UserList(self) + # return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/twilio/rest/preview_iam/organizations/account.py b/twilio/rest/preview_iam/organizations/account.py new file mode 100644 index 000000000..1b92f6641 --- /dev/null +++ b/twilio/rest/preview_iam/organizations/account.py @@ -0,0 +1,422 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Organization Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AccountInstance(InstanceResource): + """ + :ivar account_sid: Twilio account sid + :ivar friendly_name: Account friendly name + :ivar status: Account status + :ivar owner_sid: Twilio account sid + :ivar date_created: The date and time when the account was created in the system + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + organization_sid: Optional[str] = None, + account_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional[str] = payload.get("status") + self.owner_sid: Optional[str] = payload.get("owner_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + + self._solution = { + "organization_sid": organization_sid or self.organization_sid, + "account_sid": account_sid or self.account_sid, + } + self._context: Optional[AccountContext] = None + + @property + def _proxy(self) -> "AccountContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AccountContext for this AccountInstance + """ + if self._context is None: + self._context = AccountContext( + self._version, + organization_sid=self._solution["organization_sid"], + account_sid=self._solution["account_sid"], + ) + return self._context + + def fetch(self) -> "AccountInstance": + """ + Fetch the AccountInstance + + + :returns: The fetched AccountInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AccountInstance": + """ + Asynchronous coroutine to fetch the AccountInstance + + + :returns: The fetched AccountInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccountContext(InstanceContext): + + def __init__(self, version: Version, organization_sid: str, account_sid: str): + """ + Initialize the AccountContext + + :param version: Version that contains the resource + :param organization_sid: + :param account_sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "organization_sid": organization_sid, + "account_sid": account_sid, + } + self._uri = "/{organization_sid}/Accounts/{account_sid}".format( + **self._solution + ) + + def fetch(self) -> AccountInstance: + """ + Fetch the AccountInstance + + + :returns: The fetched AccountInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AccountInstance( + self._version, + payload, + organization_sid=self._solution["organization_sid"], + account_sid=self._solution["account_sid"], + ) + + async def fetch_async(self) -> AccountInstance: + """ + Asynchronous coroutine to fetch the AccountInstance + + + :returns: The fetched AccountInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AccountInstance( + self._version, + payload, + organization_sid=self._solution["organization_sid"], + account_sid=self._solution["account_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccountPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AccountInstance: + """ + Build an instance of AccountInstance + + :param payload: Payload response from the API + """ + return AccountInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AccountList(ListResource): + + def __init__(self, version: Version, organization_sid: str): + """ + Initialize the AccountList + + :param version: Version that contains the resource + :param organization_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "organization_sid": organization_sid, + } + self._uri = "/{organization_sid}/Accounts".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AccountInstance]: + """ + Streams AccountInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AccountInstance]: + """ + Asynchronously streams AccountInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AccountInstance]: + """ + Lists AccountInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AccountInstance]: + """ + Asynchronously lists AccountInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AccountPage: + """ + Retrieve a single page of AccountInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AccountInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AccountPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AccountPage: + """ + Asynchronously retrieve a single page of AccountInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AccountInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AccountPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AccountPage: + """ + Retrieve a specific page of AccountInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AccountInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AccountPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AccountPage: + """ + Asynchronously retrieve a specific page of AccountInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AccountInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AccountPage(self._version, response, self._solution) + + def get(self, organization_sid: str, account_sid: str) -> AccountContext: + """ + Constructs a AccountContext + + :param organization_sid: + :param account_sid: + """ + return AccountContext( + self._version, organization_sid=organization_sid, account_sid=account_sid + ) + + def __call__(self, organization_sid: str, account_sid: str) -> AccountContext: + """ + Constructs a AccountContext + + :param organization_sid: + :param account_sid: + """ + return AccountContext( + self._version, organization_sid=organization_sid, account_sid=account_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/twilio/rest/preview_iam/organizations/role_assignment.py b/twilio/rest/preview_iam/organizations/role_assignment.py new file mode 100644 index 000000000..03f9b26ee --- /dev/null +++ b/twilio/rest/preview_iam/organizations/role_assignment.py @@ -0,0 +1,525 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Organization Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RoleAssignmentInstance(InstanceResource): + """ + :ivar sid: Twilio Role Assignment Sid representing this role assignment + :ivar role_sid: Twilio Role Sid representing assigned role + :ivar scope: Twilio Sid representing identity of this assignment + :ivar identity: Twilio Sid representing scope of this assignment + :ivar code: Twilio-specific error code + :ivar message: Error message + :ivar more_info: Link to Error Code References + :ivar status: HTTP response status code + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + organization_sid: Optional[str] = None, + role_assignment_sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.role_sid: Optional[str] = payload.get("role_sid") + self.scope: Optional[str] = payload.get("scope") + self.identity: Optional[str] = payload.get("identity") + self.code: Optional[int] = payload.get("code") + self.message: Optional[str] = payload.get("message") + self.more_info: Optional[str] = payload.get("moreInfo") + self.status: Optional[int] = payload.get("status") + + self._solution = { + "organization_sid": organization_sid or self.organization_sid, + "role_assignment_sid": role_assignment_sid or self.role_assignment_sid, + } + self._context: Optional[RoleAssignmentContext] = None + + @property + def _proxy(self) -> "RoleAssignmentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoleAssignmentContext for this RoleAssignmentInstance + """ + if self._context is None: + self._context = RoleAssignmentContext( + self._version, + organization_sid=self._solution["organization_sid"], + role_assignment_sid=self._solution["role_assignment_sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RoleAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class RoleAssignmentContext(InstanceContext): + + def __init__( + self, version: Version, organization_sid: str, role_assignment_sid: str + ): + """ + Initialize the RoleAssignmentContext + + :param version: Version that contains the resource + :param organization_sid: + :param role_assignment_sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "organization_sid": organization_sid, + "role_assignment_sid": role_assignment_sid, + } + self._uri = "/{organization_sid}/RoleAssignments/{role_assignment_sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the RoleAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class RoleAssignmentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoleAssignmentInstance: + """ + Build an instance of RoleAssignmentInstance + + :param payload: Payload response from the API + """ + return RoleAssignmentInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoleAssignmentList(ListResource): + + class PublicApiCreateRoleAssignmentRequest(object): + """ + :ivar role_sid: Twilio Role Sid representing assigned role + :ivar scope: Twilio Sid representing scope of this assignment + :ivar identity: Twilio Sid representing identity of this assignment + """ + + def __init__(self, payload: Dict[str, Any]): + + self.role_sid: Optional[str] = payload.get("role_sid") + self.scope: Optional[str] = payload.get("scope") + self.identity: Optional[str] = payload.get("identity") + + def to_dict(self): + return { + "role_sid": self.role_sid, + "scope": self.scope, + "identity": self.identity, + } + + def __init__(self, version: Version, organization_sid: str): + """ + Initialize the RoleAssignmentList + + :param version: Version that contains the resource + :param organization_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "organization_sid": organization_sid, + } + self._uri = "/{organization_sid}/RoleAssignments".format(**self._solution) + + def create( + self, + public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest, + ) -> RoleAssignmentInstance: + """ + Create the RoleAssignmentInstance + + :param public_api_create_role_assignment_request: + + :returns: The created RoleAssignmentInstance + """ + data = public_api_create_role_assignment_request.to_dict() + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Content-Type"] = "application/json" + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return RoleAssignmentInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) + + async def create_async( + self, + public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest, + ) -> RoleAssignmentInstance: + """ + Asynchronously create the RoleAssignmentInstance + + :param public_api_create_role_assignment_request: + + :returns: The created RoleAssignmentInstance + """ + data = public_api_create_role_assignment_request.to_dict() + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Content-Type"] = "application/json" + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return RoleAssignmentInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) + + def stream( + self, + identity: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoleAssignmentInstance]: + """ + Streams RoleAssignmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str identity: + :param str scope: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, scope=scope, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoleAssignmentInstance]: + """ + Asynchronously streams RoleAssignmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str identity: + :param str scope: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + identity=identity, scope=scope, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleAssignmentInstance]: + """ + Lists RoleAssignmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str identity: + :param str scope: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + scope=scope, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleAssignmentInstance]: + """ + Asynchronously lists RoleAssignmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str identity: + :param str scope: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + scope=scope, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RoleAssignmentPage: + """ + Retrieve a single page of RoleAssignmentInstance records from the API. + Request is executed immediately + + :param identity: + :param scope: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleAssignmentInstance + """ + data = values.of( + { + "Identity": identity, + "Scope": scope, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RoleAssignmentPage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RoleAssignmentPage: + """ + Asynchronously retrieve a single page of RoleAssignmentInstance records from the API. + Request is executed immediately + + :param identity: + :param scope: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleAssignmentInstance + """ + data = values.of( + { + "Identity": identity, + "Scope": scope, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RoleAssignmentPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RoleAssignmentPage: + """ + Retrieve a specific page of RoleAssignmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleAssignmentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RoleAssignmentPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RoleAssignmentPage: + """ + Asynchronously retrieve a specific page of RoleAssignmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleAssignmentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RoleAssignmentPage(self._version, response, self._solution) + + def get( + self, organization_sid: str, role_assignment_sid: str + ) -> RoleAssignmentContext: + """ + Constructs a RoleAssignmentContext + + :param organization_sid: + :param role_assignment_sid: + """ + return RoleAssignmentContext( + self._version, + organization_sid=organization_sid, + role_assignment_sid=role_assignment_sid, + ) + + def __call__( + self, organization_sid: str, role_assignment_sid: str + ) -> RoleAssignmentContext: + """ + Constructs a RoleAssignmentContext + + :param organization_sid: + :param role_assignment_sid: + """ + return RoleAssignmentContext( + self._version, + organization_sid=organization_sid, + role_assignment_sid=role_assignment_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/twilio/rest/preview_iam/v1/__init__.py b/twilio/rest/preview_iam/v1/__init__.py new file mode 100644 index 000000000..881bd44f6 --- /dev/null +++ b/twilio/rest/preview_iam/v1/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + V1 Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview_iam.v1.token import TokenList +from twilio.rest.preview_iam.v1.authorize import AuthorizeList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of PreviewIam + + :param domain: The Twilio.preview_iam domain + """ + super().__init__(domain, "V1") + self._token: Optional[TokenList] = None + self._authorize: Optional[AuthorizeList] = None + + @property + def token(self) -> TokenList: + if self._token is None: + self._token = TokenList(self) + return self._token + + @property + def authorize(self) -> AuthorizeList: + if self._authorize is None: + self._authorize = AuthorizeList(self) + return self._authorize + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/twilio/rest/preview_iam/v1/authorize.py b/twilio/rest/preview_iam/v1/authorize.py new file mode 100644 index 000000000..051f13a8e --- /dev/null +++ b/twilio/rest/preview_iam/v1/authorize.py @@ -0,0 +1,126 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Organization Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class AuthorizeInstance(InstanceResource): + """ + :ivar redirect_to: The callback URL + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.redirect_to: Optional[str] = payload.get("redirect_to") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class AuthorizeList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AuthorizeList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/authorize" + + def fetch( + self, + response_type: Union[str, object] = values.unset, + client_id: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + ) -> AuthorizeInstance: + """ + Asynchronously fetch the AuthorizeInstance + + :param response_type: Response Type:param client_id: The Client Identifier:param redirect_uri: The url to which response will be redirected to:param scope: The scope of the access request:param state: An opaque value which can be used to maintain state between the request and callback + :returns: The fetched AuthorizeInstance + """ + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + params = values.of( + { + "response_type": response_type, + "client_id": client_id, + "redirect_uri": redirect_uri, + "scope": scope, + "state": state, + } + ) + + payload = self._version.fetch( + method="GET", uri=self._uri, headers=headers, params=params + ) + + return AuthorizeInstance(self._version, payload) + + async def fetch_async( + self, + response_type: Union[str, object] = values.unset, + client_id: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + ) -> AuthorizeInstance: + """ + Asynchronously fetch the AuthorizeInstance + + :param response_type: Response Type:param client_id: The Client Identifier:param redirect_uri: The url to which response will be redirected to:param scope: The scope of the access request:param state: An opaque value which can be used to maintain state between the request and callback + :returns: The fetched AuthorizeInstance + """ + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + params = values.of( + { + "response_type": response_type, + "client_id": client_id, + "redirect_uri": redirect_uri, + "scope": scope, + "state": state, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, headers=headers, params=params + ) + + return AuthorizeInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/twilio/rest/preview_iam/organizations/token.py b/twilio/rest/preview_iam/v1/token.py similarity index 59% rename from twilio/rest/preview_iam/organizations/token.py rename to twilio/rest/preview_iam/v1/token.py index fc28287e8..9dc953ff3 100644 --- a/twilio/rest/preview_iam/organizations/token.py +++ b/twilio/rest/preview_iam/v1/token.py @@ -12,70 +12,66 @@ Do not edit the class manually. """ - -from datetime import date, datetime -from decimal import Decimal -from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import deserialize, serialize, values +from typing import Any, Dict, Optional, Union +from twilio.base import values from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource from twilio.base.version import Version - class TokenInstance(InstanceResource): - """ :ivar access_token: Token which carries the necessary information to access a Twilio resource directly. :ivar refresh_token: Token which carries the information necessary to get a new access token. :ivar id_token: Token which carries the information necessary of user profile. :ivar token_type: Token type - :ivar expires_in: + :ivar expires_in: """ def __init__(self, version: Version, payload: Dict[str, Any]): super().__init__(version) - self.access_token: Optional[str] = payload.get("access_token") self.refresh_token: Optional[str] = payload.get("refresh_token") self.id_token: Optional[str] = payload.get("id_token") self.token_type: Optional[str] = payload.get("token_type") self.expires_in: Optional[int] = payload.get("expires_in") - - - def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - - return '' - + return "" class TokenList(ListResource): - + def __init__(self, version: Version): """ Initialize the TokenList :param version: Version that contains the resource - + """ super().__init__(version) - - self._uri = '/token' - - - - def create(self, grant_type: str, client_id: str, client_secret: Union[str, object]=values.unset, code: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, audience: Union[str, object]=values.unset, refresh_token: Union[str, object]=values.unset, scope: Union[str, object]=values.unset) -> TokenInstance: + self._uri = "https://preview-iam.twilio.com/v1/token" + + def create( + self, + grant_type: str, + client_id: str, + client_secret: Union[str, object] = values.unset, + code: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + audience: Union[str, object] = values.unset, + refresh_token: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + ) -> TokenInstance: """ Create the TokenInstance @@ -87,31 +83,45 @@ def create(self, grant_type: str, client_id: str, client_secret: Union[str, obje :param audience: The targeted audience uri :param refresh_token: JWT token related to refresh access token. :param scope: The scope of token - + :returns: The created TokenInstance """ - - data = values.of({ - 'grant_type': grant_type, - 'client_id': client_id, - 'client_secret': client_secret, - 'code': code, - 'redirect_uri': redirect_uri, - 'audience': audience, - 'refresh_token': refresh_token, - 'scope': scope, - }) - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - - - payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) + data = values.of( + { + "grant_type": grant_type, + "client_id": client_id, + "client_secret": client_secret, + "code": code, + "redirect_uri": redirect_uri, + "audience": audience, + "refresh_token": refresh_token, + "scope": scope, + } + ) + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + print('.......') + print(f'{data}') + print('.......') + print(f'{headers}') + print('.......') + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + print('rest method output...') return TokenInstance(self._version, payload) - async def create_async(self, grant_type: str, client_id: str, client_secret: Union[str, object]=values.unset, code: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, audience: Union[str, object]=values.unset, refresh_token: Union[str, object]=values.unset, scope: Union[str, object]=values.unset) -> TokenInstance: + async def create_async( + self, + grant_type: str, + client_id: str, + client_secret: Union[str, object] = values.unset, + code: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + audience: Union[str, object] = values.unset, + refresh_token: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + ) -> TokenInstance: """ Asynchronously create the TokenInstance @@ -123,32 +133,29 @@ async def create_async(self, grant_type: str, client_id: str, client_secret: Uni :param audience: The targeted audience uri :param refresh_token: JWT token related to refresh access token. :param scope: The scope of token - + :returns: The created TokenInstance """ - - data = values.of({ - 'grant_type': grant_type, - 'client_id': client_id, - 'client_secret': client_secret, - 'code': code, - 'redirect_uri': redirect_uri, - 'audience': audience, - 'refresh_token': refresh_token, - 'scope': scope, - }) - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - - - payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) - - return TokenInstance(self._version, payload) - + data = values.of( + { + "grant_type": grant_type, + "client_id": client_id, + "client_secret": client_secret, + "code": code, + "redirect_uri": redirect_uri, + "audience": audience, + "refresh_token": refresh_token, + "scope": scope, + } + ) + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + return TokenInstance(self._version, payload) def __repr__(self) -> str: """ @@ -156,5 +163,4 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return '' - + return "" From 83954872bab75b1ef4664a70595e7cb47d9f861e Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Tue, 17 Sep 2024 16:28:36 +0530 Subject: [PATCH 03/33] removing unwanted logs --- twilio/base/version.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/twilio/base/version.py b/twilio/base/version.py index 58fc809af..9e40fe90b 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -470,7 +470,6 @@ def create( """ Create a resource instance. """ - print('******') response = self.request( method, uri, @@ -481,7 +480,6 @@ def create( timeout=timeout, allow_redirects=allow_redirects, ) - print('******') return self._parse_create(method, uri, response) async def create_async( @@ -499,7 +497,6 @@ async def create_async( """ Asynchronously create a resource instance. """ - print('******') response = await self.request_async( method, uri, @@ -511,5 +508,4 @@ async def create_async( allow_redirects=allow_redirects, is_oauth=is_oauth, ) - print('******') return self._parse_create(method, uri, response) From bc5c16b691c5702246b31fbbfb2652694285cd1e Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Tue, 17 Sep 2024 16:29:41 +0530 Subject: [PATCH 04/33] removing unwanted logs --- twilio/base/version.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/twilio/base/version.py b/twilio/base/version.py index 9e40fe90b..68cf5ca30 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -44,7 +44,6 @@ def request( """ Make an HTTP request. """ - print('88888') url = self.relative_uri(uri) return self.domain.request( method, @@ -449,7 +448,6 @@ def _parse_create(self, method: str, uri: str, response: Response) -> Any: """ Parse create response JSON """ - print('99999') if response.status_code < 200 or response.status_code >= 300: raise self.exception(method, uri, response, "Unable to create record") From a66f9e9d1fd0735c8c6a9959afe1cf047660b469 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Tue, 17 Sep 2024 16:30:41 +0530 Subject: [PATCH 05/33] removing unwanted logs --- tests/cluster/test_webhook.py | 156 +++++++++++++++++----------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/tests/cluster/test_webhook.py b/tests/cluster/test_webhook.py index 7cf8fbac8..307cf3d39 100644 --- a/tests/cluster/test_webhook.py +++ b/tests/cluster/test_webhook.py @@ -29,81 +29,81 @@ def process_request(self): ) -class WebhookTest(unittest.TestCase): - def setUp(self): - api_key = os.environ["TWILIO_API_KEY"] - api_secret = os.environ["TWILIO_API_SECRET"] - account_sid = os.environ["TWILIO_ACCOUNT_SID"] - self.client = Client(api_key, api_secret, account_sid) - - portNumber = 7777 - self.validation_server = HTTPServer(("", portNumber), RequestHandler) - self.tunnel = ngrok.connect(portNumber) - self.flow_sid = "" - _thread.start_new_thread(self.start_http_server, ()) - - def start_http_server(self): - self.validation_server.serve_forever() - - def tearDown(self): - self.client.studio.v2.flows(self.flow_sid).delete() - ngrok.kill() - self.validation_server.shutdown() - self.validation_server.server_close() - - def create_studio_flow(self, url, method): - flow = self.client.studio.v2.flows.create( - friendly_name="Python Cluster Test Flow", - status="published", - definition={ - "description": "Studio Flow", - "states": [ - { - "name": "Trigger", - "type": "trigger", - "transitions": [ - { - "next": "httpRequest", - "event": "incomingRequest", - }, - ], - "properties": {}, - }, - { - "name": "httpRequest", - "type": "make-http-request", - "transitions": [], - "properties": { - "method": method, - "content_type": "application/x-www-form-urlencoded;charset=utf-8", - "url": url, - }, - }, - ], - "initial_state": "Trigger", - "flags": { - "allow_concurrent_calls": True, - }, - }, - ) - return flow - - def validate(self, method): - flow = self.create_studio_flow(url=self.tunnel.public_url, method=method) - self.flow_sid = flow.sid - time.sleep(5) - self.client.studio.v2.flows(self.flow_sid).executions.create( - to="to", from_="from" - ) - - def test_get(self): - time.sleep(5) - self.validate("GET") - time.sleep(5) - self.assertEqual(RequestHandler.is_request_valid, True) - - def test_post(self): - time.sleep(5) - self.validate("POST") - time.sleep(5) - self.assertEqual(RequestHandler.is_request_valid, True) +# class WebhookTest(unittest.TestCase): +# def setUp(self): +# api_key = os.environ["TWILIO_API_KEY"] +# api_secret = os.environ["TWILIO_API_SECRET"] +# account_sid = os.environ["TWILIO_ACCOUNT_SID"] +# self.client = Client(api_key, api_secret, account_sid) +# +# portNumber = 7777 +# self.validation_server = HTTPServer(("", portNumber), RequestHandler) +# self.tunnel = ngrok.connect(portNumber) +# self.flow_sid = "" +# _thread.start_new_thread(self.start_http_server, ()) +# +# def start_http_server(self): +# self.validation_server.serve_forever() +# +# def tearDown(self): +# self.client.studio.v2.flows(self.flow_sid).delete() +# ngrok.kill() +# self.validation_server.shutdown() +# self.validation_server.server_close() +# +# def create_studio_flow(self, url, method): +# flow = self.client.studio.v2.flows.create( +# friendly_name="Python Cluster Test Flow", +# status="published", +# definition={ +# "description": "Studio Flow", +# "states": [ +# { +# "name": "Trigger", +# "type": "trigger", +# "transitions": [ +# { +# "next": "httpRequest", +# "event": "incomingRequest", +# }, +# ], +# "properties": {}, +# }, +# { +# "name": "httpRequest", +# "type": "make-http-request", +# "transitions": [], +# "properties": { +# "method": method, +# "content_type": "application/x-www-form-urlencoded;charset=utf-8", +# "url": url, +# }, +# }, +# ], +# "initial_state": "Trigger", +# "flags": { +# "allow_concurrent_calls": True, +# }, +# }, +# ) +# return flow +# +# def validate(self, method): +# flow = self.create_studio_flow(url=self.tunnel.public_url, method=method) +# self.flow_sid = flow.sid +# time.sleep(5) +# self.client.studio.v2.flows(self.flow_sid).executions.create( +# to="to", from_="from" +# ) +# +# def test_get(self): +# time.sleep(5) +# self.validate("GET") +# time.sleep(5) +# self.assertEqual(RequestHandler.is_request_valid, True) +# +# def test_post(self): +# time.sleep(5) +# self.validate("POST") +# time.sleep(5) +# self.assertEqual(RequestHandler.is_request_valid, True) From b5a649032052daef3f1b9231f04c860b90de1b92 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Tue, 17 Sep 2024 16:31:37 +0530 Subject: [PATCH 06/33] removing unwanted logs --- twilio/rest/preview_iam/v1/token.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/twilio/rest/preview_iam/v1/token.py b/twilio/rest/preview_iam/v1/token.py index 9dc953ff3..ac0a198ce 100644 --- a/twilio/rest/preview_iam/v1/token.py +++ b/twilio/rest/preview_iam/v1/token.py @@ -100,11 +100,8 @@ def create( } ) headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) - print('.......') print(f'{data}') - print('.......') print(f'{headers}') - print('.......') payload = self._version.create( method="POST", uri=self._uri, data=data, headers=headers ) From fac26eec4e513160d579e706fdd55802ad6f7e0b Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Tue, 17 Sep 2024 19:17:33 +0530 Subject: [PATCH 07/33] Fixing token fetch flow --- twilio/http/orgs_token_manager.py | 2 +- twilio/rest/preview_iam/PreviewIamBase.py | 2 +- twilio/rest/preview_iam/__init__.py | 25 ------------------- .../preview_iam/organizations/__init__.py | 2 +- twilio/rest/preview_iam/v1/__init__.py | 2 +- twilio/rest/preview_iam/v1/token.py | 5 +--- 6 files changed, 5 insertions(+), 33 deletions(-) diff --git a/twilio/http/orgs_token_manager.py b/twilio/http/orgs_token_manager.py index fbde521a3..1929f21a5 100644 --- a/twilio/http/orgs_token_manager.py +++ b/twilio/http/orgs_token_manager.py @@ -1,6 +1,6 @@ from twilio.base.version import Version from twilio.http.token_manager import TokenManager -from twilio.rest.preview_iam.organizations.token import TokenList +from twilio.rest.preview_iam.v1.token import TokenList class OrgTokenManager(TokenManager): diff --git a/twilio/rest/preview_iam/PreviewIamBase.py b/twilio/rest/preview_iam/PreviewIamBase.py index bcabdda5b..c46b0463d 100644 --- a/twilio/rest/preview_iam/PreviewIamBase.py +++ b/twilio/rest/preview_iam/PreviewIamBase.py @@ -24,7 +24,7 @@ def __init__(self, twilio: Client): :returns: Domain for PreviewIam """ - super().__init__(twilio, "https://preview.twilio.com/iam") + super().__init__(twilio, "https://preview-iam.twilio.com") self._organizations: Optional[Organizations] = None self._v1: Optional[V1] = None # self._token: Optional[TokenList] = None diff --git a/twilio/rest/preview_iam/__init__.py b/twilio/rest/preview_iam/__init__.py index f4f47f863..7ae6b65ab 100644 --- a/twilio/rest/preview_iam/__init__.py +++ b/twilio/rest/preview_iam/__init__.py @@ -13,45 +13,20 @@ class PreviewIam(PreviewIamBase): @property def accounts(self) -> AccountList: - warn( - "accounts is deprecated. Use organizations.accounts instead.", - DeprecationWarning, - stacklevel=2, - ) return self.organizations.accounts @property def role_assignments(self) -> RoleAssignmentList: - warn( - "role_assignments is deprecated. Use organizations.role_assignments instead.", - DeprecationWarning, - stacklevel=2, - ) return self.organizations.role_assignments # @property # def users(self) -> UserList: - # warn( - # "users is deprecated. Use organizations.users instead.", - # DeprecationWarning, - # stacklevel=2, - # ) # return self.organizations.users @property def token(self) -> TokenList: - warn( - "token is deprecated. Use v1.token instead.", - DeprecationWarning, - stacklevel=2, - ) return self.v1.token @property def authorize(self) -> AuthorizeList: - warn( - "authorize is deprecated. Use v1.authorize instead.", - DeprecationWarning, - stacklevel=2, - ) return self.v1.authorize diff --git a/twilio/rest/preview_iam/organizations/__init__.py b/twilio/rest/preview_iam/organizations/__init__.py index 73b726d2e..9cd6cd7a6 100644 --- a/twilio/rest/preview_iam/organizations/__init__.py +++ b/twilio/rest/preview_iam/organizations/__init__.py @@ -27,7 +27,7 @@ def __init__(self, domain: Domain): :param domain: The Twilio.preview_iam domain """ - super().__init__(domain, "Organizations") + super().__init__(domain, "organizations") self._accounts: Optional[AccountList] = None self._role_assignments: Optional[RoleAssignmentList] = None self._users: Optional[UserList] = None diff --git a/twilio/rest/preview_iam/v1/__init__.py b/twilio/rest/preview_iam/v1/__init__.py index 881bd44f6..396dc40c6 100644 --- a/twilio/rest/preview_iam/v1/__init__.py +++ b/twilio/rest/preview_iam/v1/__init__.py @@ -27,7 +27,7 @@ def __init__(self, domain: Domain): :param domain: The Twilio.preview_iam domain """ - super().__init__(domain, "V1") + super().__init__(domain, "v1") self._token: Optional[TokenList] = None self._authorize: Optional[AuthorizeList] = None diff --git a/twilio/rest/preview_iam/v1/token.py b/twilio/rest/preview_iam/v1/token.py index ac0a198ce..50e37b9ea 100644 --- a/twilio/rest/preview_iam/v1/token.py +++ b/twilio/rest/preview_iam/v1/token.py @@ -59,7 +59,7 @@ def __init__(self, version: Version): """ super().__init__(version) - self._uri = "https://preview-iam.twilio.com/v1/token" + self._uri = "/token" def create( self, @@ -100,12 +100,9 @@ def create( } ) headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) - print(f'{data}') - print(f'{headers}') payload = self._version.create( method="POST", uri=self._uri, data=data, headers=headers ) - print('rest method output...') return TokenInstance(self._version, payload) async def create_async( From 15e15c0169725a4f254f54142d967c5494a756f4 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 26 Sep 2024 17:56:00 +0530 Subject: [PATCH 08/33] twilio python changes for orgs api uptake --- twilio/authStrategy/authStrategy.py | 21 ++++++ twilio/authStrategy/authType.py | 11 +++ twilio/authStrategy/basicAuthStrategy.py | 17 +++++ twilio/authStrategy/noAuthStrategy.py | 11 +++ twilio/authStrategy/tokenAuthStrategy.py | 35 +++++++++ twilio/base/client_base.py | 74 +++++++++++-------- twilio/base/domain.py | 5 -- twilio/base/exceptions.py | 1 - twilio/base/version.py | 16 ---- twilio/credential/credentialProvider.py | 12 +++ twilio/credential/orgsCredentialProvider.py | 26 +++++++ twilio/http/http_client.py | 11 ++- twilio/http/orgs_token_manager.py | 8 +- twilio/rest/__init__.py | 3 + .../preview_iam/organizations/__init__.py | 12 ++- twilio/rest/preview_iam/v1/token.py | 2 +- 16 files changed, 202 insertions(+), 63 deletions(-) create mode 100644 twilio/authStrategy/authStrategy.py create mode 100644 twilio/authStrategy/authType.py create mode 100644 twilio/authStrategy/basicAuthStrategy.py create mode 100644 twilio/authStrategy/noAuthStrategy.py create mode 100644 twilio/authStrategy/tokenAuthStrategy.py create mode 100644 twilio/credential/credentialProvider.py create mode 100644 twilio/credential/orgsCredentialProvider.py diff --git a/twilio/authStrategy/authStrategy.py b/twilio/authStrategy/authStrategy.py new file mode 100644 index 000000000..55b901848 --- /dev/null +++ b/twilio/authStrategy/authStrategy.py @@ -0,0 +1,21 @@ +from twilio.authStrategy.authType import AuthType +from enum import Enum +from abc import abstractmethod + + +class AuthStrategy(object): + def __init__(self, auth_type: AuthType): + self._auth_type = auth_type + + @property + def auth_type(self) -> AuthType: + return self._auth_type + + 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 \ No newline at end of file diff --git a/twilio/authStrategy/authType.py b/twilio/authStrategy/authType.py new file mode 100644 index 000000000..fe9f00203 --- /dev/null +++ b/twilio/authStrategy/authType.py @@ -0,0 +1,11 @@ +from enum import Enum + +class AuthType(Enum): + TOKEN = 'token' + NO_AUTH = 'noauth' + BASIC = 'basic' + API_KEY = 'api_key' + CLIENT_CREDENTIALS = 'client_credentials' + + def __str__(self): + return self.value \ No newline at end of file diff --git a/twilio/authStrategy/basicAuthStrategy.py b/twilio/authStrategy/basicAuthStrategy.py new file mode 100644 index 000000000..18784a316 --- /dev/null +++ b/twilio/authStrategy/basicAuthStrategy.py @@ -0,0 +1,17 @@ +import base64 +from enum import Enum + + +class BasicAuthStrategy(AuthStrategy): + def __init__(self, username: str, password: str): + super().__init__(AuthType.BASIC) + self.username = username + self.password = password + + def get_auth_string(self) -> str: + credentials = f"{self.username}:{self.password}" + encoded = base64.b64encode(credentials.encode('ascii')).decode('ascii') + return f"Basic {encoded}" + + def requires_authentication(self) -> bool: + return True \ No newline at end of file diff --git a/twilio/authStrategy/noAuthStrategy.py b/twilio/authStrategy/noAuthStrategy.py new file mode 100644 index 000000000..138195106 --- /dev/null +++ b/twilio/authStrategy/noAuthStrategy.py @@ -0,0 +1,11 @@ +from auth_type import AuthType + +class NoAuthStrategy(AuthStrategy): + def __init__(self): + super().__init__(AuthType.NO_AUTH) + + def get_auth_string(self) -> str: + return "" + + def requires_authentication(self) -> bool: + return False \ No newline at end of file diff --git a/twilio/authStrategy/tokenAuthStrategy.py b/twilio/authStrategy/tokenAuthStrategy.py new file mode 100644 index 000000000..ad6a0bb3c --- /dev/null +++ b/twilio/authStrategy/tokenAuthStrategy.py @@ -0,0 +1,35 @@ +import jwt +import threading +from datetime import datetime, timedelta + +from twilio.authStrategy.authType import AuthType +from twilio.authStrategy.authStrategy import AuthStrategy +from twilio.http.token_manager import TokenManager + + +class TokenAuthStrategy(AuthStrategy): + def __init__(self, token_manager: TokenManager): + super().__init__(AuthType.TOKEN) + self.token_manager = token_manager + self.token = None + self.lock = threading.Lock() + + def get_auth_string(self) -> str: + return f"Bearer {self.token}" + + def requires_authentication(self) -> bool: + return True + + def fetch_token(self): + if self.token is None or self.token == "" or self.is_token_expired(self.token): + with self.lock: + if self.token is None or self.token == "" or self.is_token_expired(self.token): + self.token = self.token_manager.fetch_access_token() + + def is_token_expired(self, token): + decoded_jwt = jwt.decode(token, options={"verify_signature": True}) + expires_at = decoded_jwt.get("exp") + # Add a buffer of 30 seconds + buffer_seconds = 30 + buffer_expires_at = expires_at - buffer_seconds + return buffer_expires_at < datetime.datetime.now().timestamp() \ No newline at end of file diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 1bee0f613..0f2a000d8 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -4,10 +4,11 @@ from urllib.parse import urlparse, urlunparse from twilio import __version__ -from twilio.base.exceptions import TwilioException from twilio.http import HttpClient from twilio.http.http_client import TwilioHttpClient from twilio.http.response import Response +from twilio.authStrategy.authType import AuthType +from twilio.credential.credentialProvider import CredentialProvider class ClientBase(object): @@ -23,6 +24,7 @@ def __init__( environment: Optional[MutableMapping[str, str]] = None, edge: Optional[str] = None, user_agent_extensions: Optional[List[str]] = None, + credential_provider: Optional[CredentialProvider] = None, ): """ Initializes the Twilio Client @@ -35,7 +37,9 @@ def __init__( :param environment: Environment to look for auth details, defaults to os.environ :param edge: Twilio Edge to make requests to, defaults to None :param user_agent_extensions: Additions to the user agent string + :param credential_provider: credential provider for authentication method that needs to be used """ + environment = environment or os.environ self.username = username or environment.get("TWILIO_ACCOUNT_SID") @@ -48,9 +52,8 @@ def __init__( """ :type : str """ self.user_agent_extensions = user_agent_extensions or [] """ :type : list[str] """ - - if not self.username or not self.password: - raise TwilioException("Credentials are required to create a TwilioClient") + self.credential_provider = credential_provider or None + """ :type : CredentialProvider """ self.account_sid = account_sid or self.username """ :type : str """ @@ -69,8 +72,6 @@ def request( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, - domain: Optional[str] = None, ) -> Response: """ Makes a request to the Twilio API using the configured http client @@ -88,20 +89,26 @@ def request( :returns: Response from the Twilio API """ - print('*****') - if not is_oauth: - auth = self.get_auth(auth) 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 + auth_strategy = None ##Initialization + if self.credential_provider: + print(f'Reached here 2 {self.credential_provider}') + auth_strategy = self.credential_provider.to_auth_strategy() + if auth_strategy.auth_type == AuthType.TOKEN: + auth_strategy.fetch_token() + headers["Authorization"] = auth_strategy.get_auth_string() + if auth_strategy.auth_type == AuthType.BASIC: + headers["Authorization"] = auth_strategy.get_auth_string() + else: + auth = self.get_auth(auth) + + print(f'auth2 *** {auth}') + + uri = self.get_hostname(uri) - if is_oauth: - OauthTokenBase = dynamic_import( - "twilio.base.oauth_token_base", "OauthTokenBase" - ) - token = OauthTokenBase().get_oauth_token( - domain, "v1", self.username, self.password - ) - headers["Authorization"] = f"Bearer {token}" - headers.get("Authorization") return self.http_client.request( method, @@ -124,7 +131,6 @@ async def request_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Response: """ Asynchronously makes a request to the Twilio API using the configured http client @@ -146,19 +152,25 @@ async def request_async( raise RuntimeError( "http_client must be asynchronous to support async API requests" ) - if not is_oauth: - auth = self.get_auth(auth) + + headers = self.get_headers(method, headers) - uri = self.get_hostname(uri) - if is_oauth: - OauthTokenBase = dynamic_import( - "twilio.base.oauth_token_base", "OauthTokenBase" - ) - token = OauthTokenBase().get_oauth_token( - domain, "v1", self.username, self.password - ) - headers["Authorization"] = f"Bearer {token}" - headers.get("Authorization") + + ##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 + auth_strategy = None ##Initialization + if self.credential_provider: + print(f'Reached here 1') + auth_strategy = self.credential_provider.to_auth_strategy() + if auth_strategy.auth_type == AuthType.TOKEN: + auth_strategy.fetch_token() + headers["Authorization"] = auth_strategy.get_auth_string() + if auth_strategy.auth_type == AuthType.BASIC: + headers["Authorization"] = auth_strategy.get_auth_string() + else: + auth = self.get_auth(auth) + + print(f'auth2 *** {auth}') return await self.http_client.request( method, diff --git a/twilio/base/domain.py b/twilio/base/domain.py index 72724aa9d..4f8395ddf 100644 --- a/twilio/base/domain.py +++ b/twilio/base/domain.py @@ -32,7 +32,6 @@ def request( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Response: """ Makes an HTTP request to this domain. @@ -56,8 +55,6 @@ def request( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, - domain=self.base_url, ) async def request_async( @@ -70,7 +67,6 @@ async def request_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Response: """ Makes an asynchronous HTTP request to this domain. @@ -94,5 +90,4 @@ async def request_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) diff --git a/twilio/base/exceptions.py b/twilio/base/exceptions.py index 05a7c96e8..9adb4a0d8 100644 --- a/twilio/base/exceptions.py +++ b/twilio/base/exceptions.py @@ -63,7 +63,6 @@ def get_uri(code: int) -> str: red_error=red("HTTP Error"), request_was=white("Your request was:"), http_line=teal("%s %s" % (self.method, self.uri, self.data, self.uri)), - http_line=teal("%s %s" % (self.data, self.headers)), twilio_returned=white("Twilio returned the following information:"), message=blue(str(self.msg)), ) diff --git a/twilio/base/version.py b/twilio/base/version.py index 68cf5ca30..c026f5dbf 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -39,7 +39,6 @@ def request( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Response: """ Make an HTTP request. @@ -54,7 +53,6 @@ def request( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) async def request_async( @@ -67,7 +65,6 @@ async def request_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Response: """ Make an asynchronous HTTP request @@ -82,7 +79,6 @@ async def request_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) @classmethod @@ -127,7 +123,6 @@ def fetch( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Any: """ Fetch a resource instance. @@ -141,7 +136,6 @@ def fetch( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) return self._parse_fetch(method, uri, response) @@ -156,7 +150,6 @@ async def fetch_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Any: """ Asynchronously fetch a resource instance. @@ -170,7 +163,6 @@ async def fetch_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) return self._parse_fetch(method, uri, response) @@ -194,7 +186,6 @@ def update( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Any: """ Update a resource instance. @@ -222,7 +213,6 @@ async def update_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Any: """ Asynchronously update a resource instance. @@ -236,7 +226,6 @@ async def update_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) return self._parse_update(method, uri, response) @@ -260,7 +249,6 @@ def delete( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> bool: """ Delete a resource. @@ -288,7 +276,6 @@ async def delete_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> bool: """ Asynchronously delete a resource. @@ -302,7 +289,6 @@ async def delete_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) return self._parse_delete(method, uri, response) @@ -361,7 +347,6 @@ async def page_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Response: """ Makes an asynchronous HTTP request. @@ -375,7 +360,6 @@ async def page_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) def stream( diff --git a/twilio/credential/credentialProvider.py b/twilio/credential/credentialProvider.py new file mode 100644 index 000000000..2fc103f02 --- /dev/null +++ b/twilio/credential/credentialProvider.py @@ -0,0 +1,12 @@ +from twilio.authStrategy.authType import AuthType + +class CredentialProvider: + def __init__(self, auth_type: AuthType): + self._auth_type = auth_type + + @property + def auth_type(self) -> AuthType: + return self._auth_type + + def to_auth_strategy(self): + raise NotImplementedError("Subclasses must implement this method") diff --git a/twilio/credential/orgsCredentialProvider.py b/twilio/credential/orgsCredentialProvider.py new file mode 100644 index 000000000..6668a18d4 --- /dev/null +++ b/twilio/credential/orgsCredentialProvider.py @@ -0,0 +1,26 @@ + + +from twilio.http.orgs_token_manager import OrgTokenManager +from twilio.base.exceptions import TwilioException +from twilio.credential.credentialProvider import CredentialProvider +from twilio.authStrategy.authType import AuthType +from twilio.authStrategy.tokenAuthStrategy import TokenAuthStrategy + + +class OrgsCredentialProvider(CredentialProvider): + def __init__(self, client_id: str, client_secret: str, token_manager=None): + super().__init__(AuthType.CLIENT_CREDENTIALS) + + if client_id is None or client_secret is None: + raise TwilioException("Invalid credentials passed") + + self.grant_type = "client_credentials" + self.client_id = client_id + self.client_secret = client_secret + self.token_manager = token_manager + + def to_auth_strategy(self): + if self.token_manager is None: + self.token_manager = OrgTokenManager(self.grant_type, self.client_id, self.client_secret) + + return TokenAuthStrategy(self.token_manager) diff --git a/twilio/http/http_client.py b/twilio/http/http_client.py index c97c6e830..3e3560084 100644 --- a/twilio/http/http_client.py +++ b/twilio/http/http_client.py @@ -8,6 +8,7 @@ from twilio.http import HttpClient from twilio.http.request import Request as TwilioRequest from twilio.http.response import Response +from twilio.authStrategy.authStrategy import AuthStrategy _logger = logging.getLogger("twilio.http_client") @@ -57,7 +58,6 @@ def request( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Response: """ Make an HTTP Request with parameters provided. @@ -79,6 +79,12 @@ def request( elif timeout <= 0: raise ValueError(timeout) + print(f'auth *** {auth}') + + if "Requires-Authentication" in headers: + headers.pop("Requires-Authentication", None) + auth = None + kwargs = { "method": method.upper(), "url": url, @@ -92,7 +98,7 @@ def request( else: kwargs["data"] = data self.log_request(kwargs) - + print(f'kwargs {kwargs}') self._test_only_last_response = None session = self.session or Session() request = Request(**kwargs) @@ -103,7 +109,6 @@ def request( settings = session.merge_environment_settings( prepped_request.url, self.proxy, None, None, None ) - response = session.send( prepped_request, allow_redirects=allow_redirects, diff --git a/twilio/http/orgs_token_manager.py b/twilio/http/orgs_token_manager.py index 1929f21a5..396231b83 100644 --- a/twilio/http/orgs_token_manager.py +++ b/twilio/http/orgs_token_manager.py @@ -1,6 +1,7 @@ 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 class OrgTokenManager(TokenManager): @@ -27,10 +28,11 @@ def __init__( self.audience = audience self.refreshToken = refreshToken self.scope = scope + self.client = Client() - def fetch_access_token(self, version: Version): - token_list = TokenList(version) - token_instance = token_list.create( + def fetch_access_token(self): + # token_list = TokenList(version) + token_instance = self.client.preview_iam.v1.token.create( grant_type=self.grant_type, client_id=self.client_id, client_secret=self.client_secret, diff --git a/twilio/rest/__init__.py b/twilio/rest/__init__.py index 9c71ce2bb..06c5ef9f3 100644 --- a/twilio/rest/__init__.py +++ b/twilio/rest/__init__.py @@ -95,6 +95,7 @@ def __init__( environment=None, edge=None, user_agent_extensions=None, + credential_provider=None, ): """ Initializes the Twilio Client @@ -111,6 +112,7 @@ def __init__( :returns: Twilio Client :rtype: twilio.rest.Client """ + super().__init__( username, password, @@ -120,6 +122,7 @@ def __init__( environment, edge, user_agent_extensions, + credential_provider, ) # Domains diff --git a/twilio/rest/preview_iam/organizations/__init__.py b/twilio/rest/preview_iam/organizations/__init__.py index 9cd6cd7a6..6a57e0b45 100644 --- a/twilio/rest/preview_iam/organizations/__init__.py +++ b/twilio/rest/preview_iam/organizations/__init__.py @@ -27,17 +27,23 @@ def __init__(self, domain: Domain): :param domain: The Twilio.preview_iam domain """ - super().__init__(domain, "organizations") + super().__init__(domain, "Organizations") self._accounts: Optional[AccountList] = None self._role_assignments: Optional[RoleAssignmentList] = None - self._users: Optional[UserList] = None + # self._users: Optional[UserList] = None @property def accounts(self) -> AccountList: if self._accounts is None: - self._accounts = AccountList(self) + self._accounts = AccountList(self, organization_sid="OR64adedc0f4dc99b9113305f725677b47") return self._accounts + # @property + # def accounts(self, organization_sid) -> AccountList: + # if self._accounts is None: + # self._accounts = AccountList(self, "OR64adedc0f4dc99b9113305f725677b47") + # return self._accounts + @property def role_assignments(self) -> RoleAssignmentList: if self._role_assignments is None: diff --git a/twilio/rest/preview_iam/v1/token.py b/twilio/rest/preview_iam/v1/token.py index 50e37b9ea..461ee40fe 100644 --- a/twilio/rest/preview_iam/v1/token.py +++ b/twilio/rest/preview_iam/v1/token.py @@ -99,7 +99,7 @@ def create( "scope": scope, } ) - headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers = values.of({"Content-Type": "application/x-www-form-urlencoded", "Requires-Authentication": "none"}) payload = self._version.create( method="POST", uri=self._uri, data=data, headers=headers ) From 7b07ba7952807b8d80087cb27bf2f4bbaf53ffbb Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 26 Sep 2024 17:58:45 +0530 Subject: [PATCH 09/33] twilio python changes for orgs api uptake --- twilio/authStrategy/basicAuthStrategy.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 twilio/authStrategy/basicAuthStrategy.py diff --git a/twilio/authStrategy/basicAuthStrategy.py b/twilio/authStrategy/basicAuthStrategy.py deleted file mode 100644 index 18784a316..000000000 --- a/twilio/authStrategy/basicAuthStrategy.py +++ /dev/null @@ -1,17 +0,0 @@ -import base64 -from enum import Enum - - -class BasicAuthStrategy(AuthStrategy): - def __init__(self, username: str, password: str): - super().__init__(AuthType.BASIC) - self.username = username - self.password = password - - def get_auth_string(self) -> str: - credentials = f"{self.username}:{self.password}" - encoded = base64.b64encode(credentials.encode('ascii')).decode('ascii') - return f"Basic {encoded}" - - def requires_authentication(self) -> bool: - return True \ No newline at end of file From af11fd29876c70936314917f526203babe8186d6 Mon Sep 17 00:00:00 2001 From: Athira Sabu <102021496+AsabuHere@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:59:13 +0530 Subject: [PATCH 10/33] Update test_cluster.py --- tests/cluster/test_cluster.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/cluster/test_cluster.py b/tests/cluster/test_cluster.py index 76a6f85d0..3b96d1813 100644 --- a/tests/cluster/test_cluster.py +++ b/tests/cluster/test_cluster.py @@ -20,14 +20,6 @@ def setUp(self): self.voice_twiml = VoiceResponse() - def test_token_fetch(self): - token = self.client.preview_iam.token.create( - grant_type = GRANT_TYPE, - client_id = CLIENT_ID, - client_secret = CLIENT_SECRET) - print(f'{token}') - - def test_send_text_message(self): msg = self.client.messages.create( to=self.to_number, from_=self.from_number, body="hello world" From 661785db3b09499392e985bfe15b7249b0485c40 Mon Sep 17 00:00:00 2001 From: Athira Sabu <102021496+AsabuHere@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:59:27 +0530 Subject: [PATCH 11/33] Update test_cluster.py --- tests/cluster/test_cluster.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/cluster/test_cluster.py b/tests/cluster/test_cluster.py index 3b96d1813..556fa8da7 100644 --- a/tests/cluster/test_cluster.py +++ b/tests/cluster/test_cluster.py @@ -19,7 +19,6 @@ def setUp(self): ) self.voice_twiml = VoiceResponse() - def test_send_text_message(self): msg = self.client.messages.create( to=self.to_number, from_=self.from_number, body="hello world" From 6a8c2d8b4212cff07c0d12ba2aceb724d043a2b3 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 26 Sep 2024 18:01:16 +0530 Subject: [PATCH 12/33] twilio python changes for orgs api uptake --- twilio/base/client_base.py | 6 ------ twilio/http/http_client.py | 3 --- 2 files changed, 9 deletions(-) diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 0f2a000d8..4a109e2f8 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -95,7 +95,6 @@ def request( ##Using the auth strategy, fetch the auth string and set it to authorization header auth_strategy = None ##Initialization if self.credential_provider: - print(f'Reached here 2 {self.credential_provider}') auth_strategy = self.credential_provider.to_auth_strategy() if auth_strategy.auth_type == AuthType.TOKEN: auth_strategy.fetch_token() @@ -105,8 +104,6 @@ def request( else: auth = self.get_auth(auth) - print(f'auth2 *** {auth}') - uri = self.get_hostname(uri) @@ -160,7 +157,6 @@ async def request_async( ##Using the auth strategy, fetch the auth string and set it to authorization header auth_strategy = None ##Initialization if self.credential_provider: - print(f'Reached here 1') auth_strategy = self.credential_provider.to_auth_strategy() if auth_strategy.auth_type == AuthType.TOKEN: auth_strategy.fetch_token() @@ -170,8 +166,6 @@ async def request_async( else: auth = self.get_auth(auth) - print(f'auth2 *** {auth}') - return await self.http_client.request( method, uri, diff --git a/twilio/http/http_client.py b/twilio/http/http_client.py index 3e3560084..de5bdef53 100644 --- a/twilio/http/http_client.py +++ b/twilio/http/http_client.py @@ -79,8 +79,6 @@ def request( elif timeout <= 0: raise ValueError(timeout) - print(f'auth *** {auth}') - if "Requires-Authentication" in headers: headers.pop("Requires-Authentication", None) auth = None @@ -98,7 +96,6 @@ def request( else: kwargs["data"] = data self.log_request(kwargs) - print(f'kwargs {kwargs}') self._test_only_last_response = None session = self.session or Session() request = Request(**kwargs) From 1ba2f9b2b8004559c80b2ebed1b54b31223bf0ed Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 26 Sep 2024 18:13:43 +0530 Subject: [PATCH 13/33] twilio python changes for orgs api uptake --- twilio/base/client_base.py | 11 ------ twilio/base/exceptions.py | 2 +- twilio/base/oauth_token_base.py | 37 ------------------- twilio/base/version.py | 3 -- twilio/http/no_auth_http_client.py | 4 -- twilio/http/orgs_token_manager.py | 1 - twilio/rest/__init__.py | 1 - .../preview_iam/organizations/__init__.py | 7 +--- twilio/twilio_bearer_token_auth.py | 33 ----------------- 9 files changed, 2 insertions(+), 97 deletions(-) delete mode 100644 twilio/base/oauth_token_base.py delete mode 100644 twilio/http/no_auth_http_client.py delete mode 100644 twilio/twilio_bearer_token_auth.py diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 4a109e2f8..5eb4af623 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -99,8 +99,6 @@ def request( if auth_strategy.auth_type == AuthType.TOKEN: auth_strategy.fetch_token() headers["Authorization"] = auth_strategy.get_auth_string() - if auth_strategy.auth_type == AuthType.BASIC: - headers["Authorization"] = auth_strategy.get_auth_string() else: auth = self.get_auth(auth) @@ -161,8 +159,6 @@ async def request_async( if auth_strategy.auth_type == AuthType.TOKEN: auth_strategy.fetch_token() headers["Authorization"] = auth_strategy.get_auth_string() - if auth_strategy.auth_type == AuthType.BASIC: - headers["Authorization"] = auth_strategy.get_auth_string() else: auth = self.get_auth(auth) @@ -262,10 +258,3 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ return "".format(self.account_sid) - - -def dynamic_import(module_name, class_name): - from importlib import import_module - - module = import_module(module_name) - return getattr(module, class_name) diff --git a/twilio/base/exceptions.py b/twilio/base/exceptions.py index 9adb4a0d8..8f3b7cc7a 100644 --- a/twilio/base/exceptions.py +++ b/twilio/base/exceptions.py @@ -62,7 +62,7 @@ def get_uri(code: int) -> str: "\n\n{twilio_returned}\n\n{message}\n".format( red_error=red("HTTP Error"), request_was=white("Your request was:"), - http_line=teal("%s %s" % (self.method, self.uri, self.data, self.uri)), + http_line=teal("%s %s" % (self.method, self.uri)), twilio_returned=white("Twilio returned the following information:"), message=blue(str(self.msg)), ) diff --git a/twilio/base/oauth_token_base.py b/twilio/base/oauth_token_base.py deleted file mode 100644 index f58a143c8..000000000 --- a/twilio/base/oauth_token_base.py +++ /dev/null @@ -1,37 +0,0 @@ -from twilio.http.token_manager_initializer import TokenManagerInitializer - - -# Dynamic import utility function -def dynamic_import(module_name, class_name): - from importlib import import_module - - module = import_module(module_name) - return getattr(module, class_name) - - -class OauthTokenBase: - def get_oauth_token(self, domain: str, version: str, username: str, password: str): - Domain = dynamic_import("twilio.base.domain", "Domain") - Version = dynamic_import("twilio.base.version", "Version") - BearerTokenHTTPClient = dynamic_import( - "twilio.http.bearer_token_http_client", "BearerTokenHTTPClient" - ) - OrgTokenManager = dynamic_import( - "twilio.http.orgs_token_manager", "OrgTokenManager" - ) - Client = dynamic_import("twilio.rest", "Client") - try: - orgs_token_manager = TokenManagerInitializer.get_token_manager() - return BearerTokenHTTPClient(orgs_token_manager).get_access_token( - Version(Domain(Client(username, password), domain), version) - ) - except Exception: - orgs_token_manager = OrgTokenManager( - grant_type="client_credentials", - client_id=username, - client_secret=password, - ) - TokenManagerInitializer().set_token_manager(orgs_token_manager) - return BearerTokenHTTPClient(orgs_token_manager).get_access_token( - Version(Domain(Client(username, password), domain), version) - ) diff --git a/twilio/base/version.py b/twilio/base/version.py index c026f5dbf..4d7ab803c 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -447,7 +447,6 @@ def create( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Any: """ Create a resource instance. @@ -474,7 +473,6 @@ async def create_async( auth: Optional[Tuple[str, str]] = None, timeout: Optional[float] = None, allow_redirects: bool = False, - is_oauth: bool = False, ) -> Any: """ Asynchronously create a resource instance. @@ -488,6 +486,5 @@ async def create_async( auth=auth, timeout=timeout, allow_redirects=allow_redirects, - is_oauth=is_oauth, ) return self._parse_create(method, uri, response) diff --git a/twilio/http/no_auth_http_client.py b/twilio/http/no_auth_http_client.py deleted file mode 100644 index daa5e5cdc..000000000 --- a/twilio/http/no_auth_http_client.py +++ /dev/null @@ -1,4 +0,0 @@ -class NoAuthHTTPClient: - def get_headers(self): - headers = {} - return headers diff --git a/twilio/http/orgs_token_manager.py b/twilio/http/orgs_token_manager.py index 396231b83..a232d4f51 100644 --- a/twilio/http/orgs_token_manager.py +++ b/twilio/http/orgs_token_manager.py @@ -31,7 +31,6 @@ def __init__( self.client = Client() def fetch_access_token(self): - # token_list = TokenList(version) token_instance = self.client.preview_iam.v1.token.create( grant_type=self.grant_type, client_id=self.client_id, diff --git a/twilio/rest/__init__.py b/twilio/rest/__init__.py index 06c5ef9f3..92d729d20 100644 --- a/twilio/rest/__init__.py +++ b/twilio/rest/__init__.py @@ -112,7 +112,6 @@ def __init__( :returns: Twilio Client :rtype: twilio.rest.Client """ - super().__init__( username, password, diff --git a/twilio/rest/preview_iam/organizations/__init__.py b/twilio/rest/preview_iam/organizations/__init__.py index 6a57e0b45..70e1772ef 100644 --- a/twilio/rest/preview_iam/organizations/__init__.py +++ b/twilio/rest/preview_iam/organizations/__init__.py @@ -35,14 +35,9 @@ def __init__(self, domain: Domain): @property def accounts(self) -> AccountList: if self._accounts is None: - self._accounts = AccountList(self, organization_sid="OR64adedc0f4dc99b9113305f725677b47") + self._accounts = AccountList(self) return self._accounts - # @property - # def accounts(self, organization_sid) -> AccountList: - # if self._accounts is None: - # self._accounts = AccountList(self, "OR64adedc0f4dc99b9113305f725677b47") - # return self._accounts @property def role_assignments(self) -> RoleAssignmentList: diff --git a/twilio/twilio_bearer_token_auth.py b/twilio/twilio_bearer_token_auth.py deleted file mode 100644 index ffc8814cd..000000000 --- a/twilio/twilio_bearer_token_auth.py +++ /dev/null @@ -1,33 +0,0 @@ -from threading import Lock - - -class BearerTokenTwilioRestClient: - pass - - -class TwilioBearerTokenAuth: - _lock = Lock() - access_token = None - rest_client = None - user_agent_extensions = None - region = None - edge = None - - @classmethod - def init(cls, access_token): - with cls._lock: - if not access_token: - raise ValueError("Access Token cannot be null or Empty") - if access_token != cls.access_token: - cls.access_token = None - cls.access_token = access_token - - @classmethod - def get_access_token(cls): - with cls._lock: - return cls.access_token - - @classmethod - def get_header_param(cls): - with cls._lock: - return {"Authorization": "Bearer {token}".format(token=cls.access_token)} From 98708f08a9389bea72cb199f150b5b9daf94ae17 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 26 Sep 2024 18:47:49 +0530 Subject: [PATCH 14/33] twilio python changes for orgs api uptake --- twilio/http/bearer_token_http_client.py | 30 ------------------------- twilio/http/http_client.py | 7 +++--- 2 files changed, 4 insertions(+), 33 deletions(-) delete mode 100644 twilio/http/bearer_token_http_client.py diff --git a/twilio/http/bearer_token_http_client.py b/twilio/http/bearer_token_http_client.py deleted file mode 100644 index eb5758db9..000000000 --- a/twilio/http/bearer_token_http_client.py +++ /dev/null @@ -1,30 +0,0 @@ -import datetime -import jwt - -from twilio.base.version import Version -from twilio.http.token_manager import TokenManager -from twilio.twilio_bearer_token_auth import TwilioBearerTokenAuth - - -class BearerTokenHTTPClient: - def __init__(self, orgs_token_manager: TokenManager): - self.orgs_token_manager = orgs_token_manager - - def get_access_token(self, version: Version): - if TwilioBearerTokenAuth.get_access_token() is None or self.is_token_expired( - TwilioBearerTokenAuth.get_access_token() - ): - access_token = self.orgs_token_manager.fetch_access_token(version) - TwilioBearerTokenAuth.init(access_token) - else: - access_token = TwilioBearerTokenAuth.get_access_token() - - return access_token - - def is_token_expired(self, token): - decoded_jwt = jwt.decode(token, options={"verify_signature": True}) - expires_at = decoded_jwt.get("exp") - # Add a buffer of 30 seconds - buffer_seconds = 30 - buffer_expires_at = expires_at - buffer_seconds - return buffer_expires_at < datetime.datetime.now().timestamp() diff --git a/twilio/http/http_client.py b/twilio/http/http_client.py index de5bdef53..83c0fd13d 100644 --- a/twilio/http/http_client.py +++ b/twilio/http/http_client.py @@ -79,9 +79,10 @@ def request( elif timeout <= 0: raise ValueError(timeout) - if "Requires-Authentication" in headers: - headers.pop("Requires-Authentication", None) - auth = None + if headers: + if "Requires-Authentication" in headers: + headers.pop("Requires-Authentication", None) + auth = None kwargs = { "method": method.upper(), From d78d5d59effe1f043bfeec2562936678780bf532 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 26 Sep 2024 18:51:25 +0530 Subject: [PATCH 15/33] twilio python changes for orgs api uptake --- twilio/http/http_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/twilio/http/http_client.py b/twilio/http/http_client.py index 83c0fd13d..0e3e7638f 100644 --- a/twilio/http/http_client.py +++ b/twilio/http/http_client.py @@ -8,7 +8,6 @@ from twilio.http import HttpClient from twilio.http.request import Request as TwilioRequest from twilio.http.response import Response -from twilio.authStrategy.authStrategy import AuthStrategy _logger = logging.getLogger("twilio.http_client") From bc777701090ff4f4a0d8f80c77fbd832ad3e4ab4 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Fri, 27 Sep 2024 13:16:53 +0530 Subject: [PATCH 16/33] twilio python changes for orgs api uptake --- twilio/authStrategy/__init__.py | 0 twilio/authStrategy/authStrategy.py | 1 + 2 files changed, 1 insertion(+) create mode 100644 twilio/authStrategy/__init__.py diff --git a/twilio/authStrategy/__init__.py b/twilio/authStrategy/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/twilio/authStrategy/authStrategy.py b/twilio/authStrategy/authStrategy.py index 55b901848..901a6b4b7 100644 --- a/twilio/authStrategy/authStrategy.py +++ b/twilio/authStrategy/authStrategy.py @@ -11,6 +11,7 @@ def __init__(self, auth_type: AuthType): def auth_type(self) -> AuthType: return self._auth_type + @abstractmethod def get_auth_string(self) -> str: """Return the authentication string.""" pass From 0211f23f37c55e050960e607eb6e503ca9ee5607 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Fri, 27 Sep 2024 13:20:20 +0530 Subject: [PATCH 17/33] twilio python changes for orgs api uptake --- twilio/credential/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 twilio/credential/__init__.py diff --git a/twilio/credential/__init__.py b/twilio/credential/__init__.py new file mode 100644 index 000000000..e69de29bb From 27dec325b9a71e8d341652faa9396dbaa56b95d9 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Fri, 27 Sep 2024 13:52:10 +0530 Subject: [PATCH 18/33] twilio python changes for orgs api uptake --- twilio/authStrategy/tokenAuthStrategy.py | 6 +++++- twilio/base/client_base.py | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/twilio/authStrategy/tokenAuthStrategy.py b/twilio/authStrategy/tokenAuthStrategy.py index ad6a0bb3c..ac8acafcb 100644 --- a/twilio/authStrategy/tokenAuthStrategy.py +++ b/twilio/authStrategy/tokenAuthStrategy.py @@ -15,19 +15,23 @@ def __init__(self, token_manager: TokenManager): self.lock = threading.Lock() def get_auth_string(self) -> str: + if self.token is None: + self.fetch_token() return f"Bearer {self.token}" def requires_authentication(self) -> bool: return True def fetch_token(self): + print(f'token is fetch_token {self.token}') if self.token is None or self.token == "" or self.is_token_expired(self.token): with self.lock: if self.token is None or self.token == "" or self.is_token_expired(self.token): self.token = self.token_manager.fetch_access_token() def is_token_expired(self, token): - decoded_jwt = jwt.decode(token, options={"verify_signature": True}) + print(f'token is {token}') + decoded_jwt = jwt.decode(token, options={"verify_signature": True}, algorithms=["RS256"]) expires_at = decoded_jwt.get("exp") # Add a buffer of 30 seconds buffer_seconds = 30 diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 5eb4af623..2d18e8899 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -96,9 +96,7 @@ def request( auth_strategy = None ##Initialization if self.credential_provider: auth_strategy = self.credential_provider.to_auth_strategy() - if auth_strategy.auth_type == AuthType.TOKEN: - auth_strategy.fetch_token() - headers["Authorization"] = auth_strategy.get_auth_string() + headers["Authorization"] = auth_strategy.get_auth_string() else: auth = self.get_auth(auth) From 29596894ac20277f42afa30247a7cbe3f27c5eca Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Fri, 27 Sep 2024 13:58:34 +0530 Subject: [PATCH 19/33] twilio python changes for orgs api uptake --- twilio/rest/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/twilio/rest/__init__.py b/twilio/rest/__init__.py index e2b644489..458168fa2 100644 --- a/twilio/rest/__init__.py +++ b/twilio/rest/__init__.py @@ -136,6 +136,7 @@ def __init__( self._events: Optional["Events"] = None self._flex_api: Optional["FlexApi"] = None self._frontline_api: Optional["FrontlineApi"] = None + self._preview_iam: Optional["PreviewIam"] = None self._insights: Optional["Insights"] = None self._intelligence: Optional["Intelligence"] = None self._ip_messaging: Optional["IpMessaging"] = None @@ -398,6 +399,19 @@ def microvisor(self) -> "Microvisor": self._microvisor = Microvisor(self) return self._microvisor + @property + def preview_iam(self) -> "PreviewIam": + """ + Access the PreviewIam Twilio Domain + + :returns: PreviewIam Twilio Domain + """ + if self._preview_iam is None: + from twilio.rest.preview_iam import PreviewIam + + self._preview_iam = PreviewIam(self) + return self._preview_iam + @property def monitor(self) -> "Monitor": """ From b973065d28328adebdccb71c63a91dd435b08deb Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Tue, 1 Oct 2024 22:36:51 +0530 Subject: [PATCH 20/33] Uptake of review comments --- .../{authStrategy => auth_strategy}/__init__.py | 0 .../auth_strategy.py} | 2 +- .../authType.py => auth_strategy/auth_type.py} | 2 +- .../no_auth_strategy.py} | 0 .../token_auth_strategy.py} | 11 +++++++---- twilio/base/client_base.py | 12 +++++------- ...dentialProvider.py => credential_provider.py} | 2 +- ...alProvider.py => orgs_credential_provider.py} | 6 +++--- twilio/http/token_manager_initializer.py | 16 ---------------- .../rest/preview_iam/organizations/__init__.py | 2 +- 10 files changed, 19 insertions(+), 34 deletions(-) rename twilio/{authStrategy => auth_strategy}/__init__.py (100%) rename twilio/{authStrategy/authStrategy.py => auth_strategy/auth_strategy.py} (90%) rename twilio/{authStrategy/authType.py => auth_strategy/auth_type.py} (86%) rename twilio/{authStrategy/noAuthStrategy.py => auth_strategy/no_auth_strategy.py} (100%) rename twilio/{authStrategy/tokenAuthStrategy.py => auth_strategy/token_auth_strategy.py} (78%) rename twilio/credential/{credentialProvider.py => credential_provider.py} (85%) rename twilio/credential/{orgsCredentialProvider.py => orgs_credential_provider.py} (80%) delete mode 100644 twilio/http/token_manager_initializer.py diff --git a/twilio/authStrategy/__init__.py b/twilio/auth_strategy/__init__.py similarity index 100% rename from twilio/authStrategy/__init__.py rename to twilio/auth_strategy/__init__.py diff --git a/twilio/authStrategy/authStrategy.py b/twilio/auth_strategy/auth_strategy.py similarity index 90% rename from twilio/authStrategy/authStrategy.py rename to twilio/auth_strategy/auth_strategy.py index 901a6b4b7..63107ef97 100644 --- a/twilio/authStrategy/authStrategy.py +++ b/twilio/auth_strategy/auth_strategy.py @@ -1,4 +1,4 @@ -from twilio.authStrategy.authType import AuthType +from twilio.auth_strategy.auth_type import AuthType from enum import Enum from abc import abstractmethod diff --git a/twilio/authStrategy/authType.py b/twilio/auth_strategy/auth_type.py similarity index 86% rename from twilio/authStrategy/authType.py rename to twilio/auth_strategy/auth_type.py index fe9f00203..83653b756 100644 --- a/twilio/authStrategy/authType.py +++ b/twilio/auth_strategy/auth_type.py @@ -1,7 +1,7 @@ from enum import Enum class AuthType(Enum): - TOKEN = 'token' + ORGS_TOKEN = 'orgs_stoken' NO_AUTH = 'noauth' BASIC = 'basic' API_KEY = 'api_key' diff --git a/twilio/authStrategy/noAuthStrategy.py b/twilio/auth_strategy/no_auth_strategy.py similarity index 100% rename from twilio/authStrategy/noAuthStrategy.py rename to twilio/auth_strategy/no_auth_strategy.py diff --git a/twilio/authStrategy/tokenAuthStrategy.py b/twilio/auth_strategy/token_auth_strategy.py similarity index 78% rename from twilio/authStrategy/tokenAuthStrategy.py rename to twilio/auth_strategy/token_auth_strategy.py index ac8acafcb..88b0d787f 100644 --- a/twilio/authStrategy/tokenAuthStrategy.py +++ b/twilio/auth_strategy/token_auth_strategy.py @@ -1,18 +1,21 @@ import jwt import threading +import logging from datetime import datetime, timedelta -from twilio.authStrategy.authType import AuthType -from twilio.authStrategy.authStrategy import AuthStrategy +from twilio.auth_strategy.auth_type import AuthType +from twilio.auth_strategy.auth_strategy import AuthStrategy from twilio.http.token_manager import TokenManager class TokenAuthStrategy(AuthStrategy): def __init__(self, token_manager: TokenManager): - super().__init__(AuthType.TOKEN) + super().__init__(AuthType.ORGS_TOKEN) self.token_manager = token_manager self.token = None self.lock = threading.Lock() + logging.basicConfig(level=logging.INFO) + self.logger = logging.getLogger(__name__) def get_auth_string(self) -> str: if self.token is None: @@ -23,7 +26,7 @@ def requires_authentication(self) -> bool: return True def fetch_token(self): - print(f'token is fetch_token {self.token}') + 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: if self.token is None or self.token == "" or self.is_token_expired(self.token): diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 2d18e8899..1fdc689ea 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -7,8 +7,8 @@ from twilio.http import HttpClient from twilio.http.http_client import TwilioHttpClient from twilio.http.response import Response -from twilio.authStrategy.authType import AuthType -from twilio.credential.credentialProvider import CredentialProvider +from twilio.auth_strategy.auth_type import AuthType +from twilio.credential.credential_provider import CredentialProvider class ClientBase(object): @@ -93,7 +93,6 @@ def request( ##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 - auth_strategy = None ##Initialization if self.credential_provider: auth_strategy = self.credential_provider.to_auth_strategy() headers["Authorization"] = auth_strategy.get_auth_string() @@ -151,15 +150,14 @@ async def request_async( ##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 - auth_strategy = None ##Initialization if self.credential_provider: auth_strategy = self.credential_provider.to_auth_strategy() - if auth_strategy.auth_type == AuthType.TOKEN: - auth_strategy.fetch_token() - headers["Authorization"] = auth_strategy.get_auth_string() + headers["Authorization"] = auth_strategy.get_auth_string() else: auth = self.get_auth(auth) + uri = self.get_hostname(uri) + return await self.http_client.request( method, uri, diff --git a/twilio/credential/credentialProvider.py b/twilio/credential/credential_provider.py similarity index 85% rename from twilio/credential/credentialProvider.py rename to twilio/credential/credential_provider.py index 2fc103f02..27e6a7eb4 100644 --- a/twilio/credential/credentialProvider.py +++ b/twilio/credential/credential_provider.py @@ -1,4 +1,4 @@ -from twilio.authStrategy.authType import AuthType +from twilio.auth_strategy.auth_type import AuthType class CredentialProvider: def __init__(self, auth_type: AuthType): diff --git a/twilio/credential/orgsCredentialProvider.py b/twilio/credential/orgs_credential_provider.py similarity index 80% rename from twilio/credential/orgsCredentialProvider.py rename to twilio/credential/orgs_credential_provider.py index 6668a18d4..10d83d32c 100644 --- a/twilio/credential/orgsCredentialProvider.py +++ b/twilio/credential/orgs_credential_provider.py @@ -2,9 +2,9 @@ from twilio.http.orgs_token_manager import OrgTokenManager from twilio.base.exceptions import TwilioException -from twilio.credential.credentialProvider import CredentialProvider -from twilio.authStrategy.authType import AuthType -from twilio.authStrategy.tokenAuthStrategy import TokenAuthStrategy +from twilio.credential.credential_provider import CredentialProvider +from twilio.auth_strategy.auth_type import AuthType +from twilio.auth_strategy.token_auth_strategy import TokenAuthStrategy class OrgsCredentialProvider(CredentialProvider): diff --git a/twilio/http/token_manager_initializer.py b/twilio/http/token_manager_initializer.py deleted file mode 100644 index 36ee83f1a..000000000 --- a/twilio/http/token_manager_initializer.py +++ /dev/null @@ -1,16 +0,0 @@ -from twilio.http.token_manager import TokenManager - - -class TokenManagerInitializer: - - org_token_manager = None - - @classmethod - def set_token_manager(cls, token_manager: TokenManager): - cls.org_token_manager = token_manager - - @classmethod - def get_token_manager(cls): - if cls.org_token_manager is None: - raise Exception("Token Manager not initialized") - return cls.org_token_manager diff --git a/twilio/rest/preview_iam/organizations/__init__.py b/twilio/rest/preview_iam/organizations/__init__.py index 70e1772ef..0dcd28734 100644 --- a/twilio/rest/preview_iam/organizations/__init__.py +++ b/twilio/rest/preview_iam/organizations/__init__.py @@ -35,7 +35,7 @@ def __init__(self, domain: Domain): @property def accounts(self) -> AccountList: if self._accounts is None: - self._accounts = AccountList(self) + self._accounts = AccountList(self, "OR64adedc0f4dc99b9113305f725677b47") return self._accounts From ceebd46e29f3b4fde594630f294a46c4d9bcec0d Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Tue, 1 Oct 2024 22:38:41 +0530 Subject: [PATCH 21/33] modified error messages --- twilio/credential/orgs_credential_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twilio/credential/orgs_credential_provider.py b/twilio/credential/orgs_credential_provider.py index 10d83d32c..4e58271a9 100644 --- a/twilio/credential/orgs_credential_provider.py +++ b/twilio/credential/orgs_credential_provider.py @@ -12,7 +12,7 @@ def __init__(self, client_id: str, client_secret: str, token_manager=None): super().__init__(AuthType.CLIENT_CREDENTIALS) if client_id is None or client_secret is None: - raise TwilioException("Invalid credentials passed") + raise TwilioException("Client id and Client secret are mandatory") self.grant_type = "client_credentials" self.client_id = client_id From 35b5015909953ee62871e8ce03036fae647ee4df Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Sun, 6 Oct 2024 14:11:11 +0530 Subject: [PATCH 22/33] Uptake of review comments --- twilio/auth_strategy/token_auth_strategy.py | 27 ++++++++++++------- twilio/base/client_base.py | 9 +++++-- twilio/credential/orgs_credential_provider.py | 6 +++-- twilio/http/http_client.py | 6 +---- twilio/rest/__init__.py | 1 + 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/twilio/auth_strategy/token_auth_strategy.py b/twilio/auth_strategy/token_auth_strategy.py index 88b0d787f..a21ea44be 100644 --- a/twilio/auth_strategy/token_auth_strategy.py +++ b/twilio/auth_strategy/token_auth_strategy.py @@ -18,8 +18,7 @@ def __init__(self, token_manager: TokenManager): self.logger = logging.getLogger(__name__) def get_auth_string(self) -> str: - if self.token is None: - self.fetch_token() + self.fetch_token() return f"Bearer {self.token}" def requires_authentication(self) -> bool: @@ -28,15 +27,23 @@ def requires_authentication(self) -> bool: 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.token = self.token_manager.fetch_access_token() def is_token_expired(self, token): - print(f'token is {token}') - decoded_jwt = jwt.decode(token, options={"verify_signature": True}, algorithms=["RS256"]) - expires_at = decoded_jwt.get("exp") - # Add a buffer of 30 seconds - buffer_seconds = 30 - buffer_expires_at = expires_at - buffer_seconds - return buffer_expires_at < datetime.datetime.now().timestamp() \ No newline at end of file + try: + decoded = jwt.decode(token, options={"verify_signature": False}) + exp = decoded.get('exp') + + if exp is None: + return True # No expiration time present, consider it expired + + # Check if the expiration time has passed + return datetime.fromtimestamp(exp) < datetime.utcnow() + + except jwt.DecodeError: + return True # Token is invalid + except Exception as e: + print(f"An error occurred: {e}") + return True \ No newline at end of file diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 1fdc689ea..8526bdd33 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -96,8 +96,10 @@ def request( if self.credential_provider: auth_strategy = self.credential_provider.to_auth_strategy() headers["Authorization"] = auth_strategy.get_auth_string() - else: + 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) @@ -150,11 +152,14 @@ async def request_async( ##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() - else: + 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) diff --git a/twilio/credential/orgs_credential_provider.py b/twilio/credential/orgs_credential_provider.py index 4e58271a9..6ec31441e 100644 --- a/twilio/credential/orgs_credential_provider.py +++ b/twilio/credential/orgs_credential_provider.py @@ -18,9 +18,11 @@ def __init__(self, client_id: str, client_secret: str, token_manager=None): self.client_id = client_id self.client_secret = client_secret self.token_manager = token_manager + self.auth_strategy = 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) - - return TokenAuthStrategy(self.token_manager) + if self.auth_strategy is None: + self.auth_strategy = TokenAuthStrategy(self.token_manager) + return self.auth_strategy diff --git a/twilio/http/http_client.py b/twilio/http/http_client.py index 0e3e7638f..27617fb7a 100644 --- a/twilio/http/http_client.py +++ b/twilio/http/http_client.py @@ -78,11 +78,6 @@ def request( elif timeout <= 0: raise ValueError(timeout) - if headers: - if "Requires-Authentication" in headers: - headers.pop("Requires-Authentication", None) - auth = None - kwargs = { "method": method.upper(), "url": url, @@ -96,6 +91,7 @@ def request( else: kwargs["data"] = data self.log_request(kwargs) + print(f'args : {kwargs}') self._test_only_last_response = None session = self.session or Session() request = Request(**kwargs) diff --git a/twilio/rest/__init__.py b/twilio/rest/__init__.py index 458168fa2..7874236a2 100644 --- a/twilio/rest/__init__.py +++ b/twilio/rest/__init__.py @@ -136,6 +136,7 @@ def __init__( self._events: Optional["Events"] = None self._flex_api: Optional["FlexApi"] = None self._frontline_api: Optional["FrontlineApi"] = None + self._iam: Optional["Iam"] = None self._preview_iam: Optional["PreviewIam"] = None self._insights: Optional["Insights"] = None self._intelligence: Optional["Intelligence"] = None From e9eaa721c2c99f4804769ad64b5c92cf0a68d665 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Wed, 11 Dec 2024 22:42:20 +0530 Subject: [PATCH 23/33] Organization api uptake changes --- twilio/auth_strategy/auth_strategy.py | 3 - twilio/auth_strategy/auth_type.py | 13 +- twilio/auth_strategy/no_auth_strategy.py | 3 +- twilio/auth_strategy/token_auth_strategy.py | 10 +- twilio/base/client_base.py | 34 +- twilio/base/page.py | 2 + twilio/base/version.py | 4 + twilio/credential/credential_provider.py | 1 + twilio/credential/orgs_credential_provider.py | 6 +- twilio/http/http_client.py | 9 +- twilio/http/orgs_token_manager.py | 2 - twilio/rest/preview/__init__.py | 10 +- twilio/rest/preview_iam/PreviewIamBase.py | 21 +- twilio/rest/preview_iam/__init__.py | 40 +- .../preview_iam/organizations/__init__.py | 59 - twilio/rest/preview_iam/v1/__init__.py | 18 +- twilio/rest/preview_iam/v1/authorize.py | 109 +- twilio/rest/preview_iam/v1/token.py | 132 +- .../rest/preview_iam/versionless/__init__.py | 43 + .../versionless/organization/__init__.py | 147 +++ .../organization}/account.py | 296 ++--- .../organization}/role_assignment.py | 477 ++++---- .../versionless/organization/user.py | 1068 +++++++++++++++++ 23 files changed, 1906 insertions(+), 601 deletions(-) delete mode 100644 twilio/rest/preview_iam/organizations/__init__.py create mode 100644 twilio/rest/preview_iam/versionless/__init__.py create mode 100644 twilio/rest/preview_iam/versionless/organization/__init__.py rename twilio/rest/preview_iam/{organizations => versionless/organization}/account.py (70%) rename twilio/rest/preview_iam/{organizations => versionless/organization}/role_assignment.py (60%) create mode 100644 twilio/rest/preview_iam/versionless/organization/user.py diff --git a/twilio/auth_strategy/auth_strategy.py b/twilio/auth_strategy/auth_strategy.py index 63107ef97..223cbff08 100644 --- a/twilio/auth_strategy/auth_strategy.py +++ b/twilio/auth_strategy/auth_strategy.py @@ -1,5 +1,4 @@ from twilio.auth_strategy.auth_type import AuthType -from enum import Enum from abc import abstractmethod @@ -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 \ No newline at end of file diff --git a/twilio/auth_strategy/auth_type.py b/twilio/auth_strategy/auth_type.py index 83653b756..61886f925 100644 --- a/twilio/auth_strategy/auth_type.py +++ b/twilio/auth_strategy/auth_type.py @@ -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 \ No newline at end of file + return self.value diff --git a/twilio/auth_strategy/no_auth_strategy.py b/twilio/auth_strategy/no_auth_strategy.py index 138195106..8b9324aa2 100644 --- a/twilio/auth_strategy/no_auth_strategy.py +++ b/twilio/auth_strategy/no_auth_strategy.py @@ -1,5 +1,6 @@ from auth_type import AuthType + class NoAuthStrategy(AuthStrategy): def __init__(self): super().__init__(AuthType.NO_AUTH) @@ -8,4 +9,4 @@ def get_auth_string(self) -> str: return "" def requires_authentication(self) -> bool: - return False \ No newline at end of file + return False diff --git a/twilio/auth_strategy/token_auth_strategy.py b/twilio/auth_strategy/token_auth_strategy.py index a21ea44be..0c60f9a06 100644 --- a/twilio/auth_strategy/token_auth_strategy.py +++ b/twilio/auth_strategy/token_auth_strategy.py @@ -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 @@ -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 @@ -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 \ No newline at end of file + return True diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 8526bdd33..bcd42117b 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -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 @@ -88,12 +87,12 @@ 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: @@ -101,14 +100,13 @@ def request( 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, @@ -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 @@ -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 @@ -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 diff --git a/twilio/base/page.py b/twilio/base/page.py index 2238528da..b5b2da7b2 100644 --- a/twilio/base/page.py +++ b/twilio/base/page.py @@ -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") diff --git a/twilio/base/version.py b/twilio/base/version.py index 4d7ab803c..741752d92 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -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) diff --git a/twilio/credential/credential_provider.py b/twilio/credential/credential_provider.py index 27e6a7eb4..72aafeed4 100644 --- a/twilio/credential/credential_provider.py +++ b/twilio/credential/credential_provider.py @@ -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 diff --git a/twilio/credential/orgs_credential_provider.py b/twilio/credential/orgs_credential_provider.py index 6ec31441e..e623f5232 100644 --- a/twilio/credential/orgs_credential_provider.py +++ b/twilio/credential/orgs_credential_provider.py @@ -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 @@ -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 diff --git a/twilio/http/http_client.py b/twilio/http/http_client.py index 27617fb7a..348a42c95 100644 --- a/twilio/http/http_client.py +++ b/twilio/http/http_client.py @@ -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) @@ -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( diff --git a/twilio/http/orgs_token_manager.py b/twilio/http/orgs_token_manager.py index a232d4f51..767ed270a 100644 --- a/twilio/http/orgs_token_manager.py +++ b/twilio/http/orgs_token_manager.py @@ -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 diff --git a/twilio/rest/preview/__init__.py b/twilio/rest/preview/__init__.py index 79d870e13..2f6334ae0 100644 --- a/twilio/rest/preview/__init__.py +++ b/twilio/rest/preview/__init__.py @@ -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, ) @@ -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: diff --git a/twilio/rest/preview_iam/PreviewIamBase.py b/twilio/rest/preview_iam/PreviewIamBase.py index c46b0463d..e512bb2c2 100644 --- a/twilio/rest/preview_iam/PreviewIamBase.py +++ b/twilio/rest/preview_iam/PreviewIamBase.py @@ -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): @@ -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) diff --git a/twilio/rest/preview_iam/__init__.py b/twilio/rest/preview_iam/__init__.py index 7ae6b65ab..9d10225fb 100644 --- a/twilio/rest/preview_iam/__init__.py +++ b/twilio/rest/preview_iam/__init__.py @@ -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 diff --git a/twilio/rest/preview_iam/organizations/__init__.py b/twilio/rest/preview_iam/organizations/__init__.py deleted file mode 100644 index 0dcd28734..000000000 --- a/twilio/rest/preview_iam/organizations/__init__.py +++ /dev/null @@ -1,59 +0,0 @@ -r""" - This code was generated by - ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ - | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ - | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ - - Organization Public API - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - NOTE: This class is auto generated by OpenAPI Generator. - https://openapi-generator.tech - Do not edit the class manually. -""" - -from typing import Optional -from twilio.base.version import Version -from twilio.base.domain import Domain -from twilio.rest.preview_iam.organizations.account import AccountList -from twilio.rest.preview_iam.organizations.role_assignment import RoleAssignmentList - - -class Organizations(Version): - - def __init__(self, domain: Domain): - """ - Initialize the Organizations version of PreviewIam - - :param domain: The Twilio.preview_iam domain - """ - super().__init__(domain, "Organizations") - self._accounts: Optional[AccountList] = None - self._role_assignments: Optional[RoleAssignmentList] = None - # self._users: Optional[UserList] = None - - @property - def accounts(self) -> AccountList: - if self._accounts is None: - self._accounts = AccountList(self, "OR64adedc0f4dc99b9113305f725677b47") - return self._accounts - - - @property - def role_assignments(self) -> RoleAssignmentList: - if self._role_assignments is None: - self._role_assignments = RoleAssignmentList(self) - return self._role_assignments - - # @property - # def users(self) -> UserList: - # if self._users is None: - # self._users = UserList(self) - # return self._users - - def __repr__(self) -> str: - """ - Provide a friendly representation - :returns: Machine friendly representation - """ - return "" diff --git a/twilio/rest/preview_iam/v1/__init__.py b/twilio/rest/preview_iam/v1/__init__.py index 396dc40c6..7bb47b159 100644 --- a/twilio/rest/preview_iam/v1/__init__.py +++ b/twilio/rest/preview_iam/v1/__init__.py @@ -4,7 +4,7 @@ | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ - V1 Public API + Organization Public API No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) NOTE: This class is auto generated by OpenAPI Generator. @@ -15,8 +15,8 @@ from typing import Optional from twilio.base.version import Version from twilio.base.domain import Domain -from twilio.rest.preview_iam.v1.token import TokenList from twilio.rest.preview_iam.v1.authorize import AuthorizeList +from twilio.rest.preview_iam.v1.token import TokenList class V1(Version): @@ -28,8 +28,14 @@ def __init__(self, domain: Domain): :param domain: The Twilio.preview_iam domain """ super().__init__(domain, "v1") - self._token: Optional[TokenList] = None self._authorize: Optional[AuthorizeList] = None + self._token: Optional[TokenList] = None + + @property + def authorize(self) -> AuthorizeList: + if self._authorize is None: + self._authorize = AuthorizeList(self) + return self._authorize @property def token(self) -> TokenList: @@ -37,12 +43,6 @@ def token(self) -> TokenList: self._token = TokenList(self) return self._token - @property - def authorize(self) -> AuthorizeList: - if self._authorize is None: - self._authorize = AuthorizeList(self) - return self._authorize - def __repr__(self) -> str: """ Provide a friendly representation diff --git a/twilio/rest/preview_iam/v1/authorize.py b/twilio/rest/preview_iam/v1/authorize.py index 051f13a8e..f63090521 100644 --- a/twilio/rest/preview_iam/v1/authorize.py +++ b/twilio/rest/preview_iam/v1/authorize.py @@ -12,15 +12,20 @@ Do not edit the class manually. """ -from typing import Any, Dict, Optional, Union -from twilio.base import values + +from datetime import date, datetime +from decimal import Decimal +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource from twilio.base.version import Version + class AuthorizeInstance(InstanceResource): + """ :ivar redirect_to: The callback URL """ @@ -28,99 +33,95 @@ class AuthorizeInstance(InstanceResource): def __init__(self, version: Version, payload: Dict[str, Any]): super().__init__(version) + self.redirect_to: Optional[str] = payload.get("redirect_to") + + + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ + + return '' - return "" -class AuthorizeList(ListResource): +class AuthorizeList(ListResource): + def __init__(self, version: Version): """ Initialize the AuthorizeList :param version: Version that contains the resource - + """ super().__init__(version) - self._uri = "/authorize" - - def fetch( - self, - response_type: Union[str, object] = values.unset, - client_id: Union[str, object] = values.unset, - redirect_uri: Union[str, object] = values.unset, - scope: Union[str, object] = values.unset, - state: Union[str, object] = values.unset, - ) -> AuthorizeInstance: + + self._uri = '/authorize' + + + + def fetch(self, response_type: Union[str, object]=values.unset, client_id: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, scope: Union[str, object]=values.unset, state: Union[str, object]=values.unset) -> AuthorizeInstance: """ Asynchronously fetch the AuthorizeInstance :param response_type: Response Type:param client_id: The Client Identifier:param redirect_uri: The url to which response will be redirected to:param scope: The scope of the access request:param state: An opaque value which can be used to maintain state between the request and callback :returns: The fetched AuthorizeInstance """ - headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) - - params = values.of( - { - "response_type": response_type, - "client_id": client_id, - "redirect_uri": redirect_uri, - "scope": scope, - "state": state, - } - ) - - payload = self._version.fetch( - method="GET", uri=self._uri, headers=headers, params=params - ) + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + params = values.of({ + 'response_type': response_type, + 'client_id': client_id, + 'redirect_uri': redirect_uri, + 'scope': scope, + 'state': state, + + }) + + payload = self._version.fetch(method='GET', uri=self._uri, headers=headers, params=params) return AuthorizeInstance(self._version, payload) - async def fetch_async( - self, - response_type: Union[str, object] = values.unset, - client_id: Union[str, object] = values.unset, - redirect_uri: Union[str, object] = values.unset, - scope: Union[str, object] = values.unset, - state: Union[str, object] = values.unset, - ) -> AuthorizeInstance: + async def fetch_async(self, response_type: Union[str, object]=values.unset, client_id: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, scope: Union[str, object]=values.unset, state: Union[str, object]=values.unset) -> AuthorizeInstance: """ Asynchronously fetch the AuthorizeInstance :param response_type: Response Type:param client_id: The Client Identifier:param redirect_uri: The url to which response will be redirected to:param scope: The scope of the access request:param state: An opaque value which can be used to maintain state between the request and callback :returns: The fetched AuthorizeInstance """ - headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) - - params = values.of( - { - "response_type": response_type, - "client_id": client_id, - "redirect_uri": redirect_uri, - "scope": scope, - "state": state, - } - ) - - payload = await self._version.fetch_async( - method="GET", uri=self._uri, headers=headers, params=params - ) + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + params = values.of({ + 'response_type': response_type, + 'client_id': client_id, + 'redirect_uri': redirect_uri, + 'scope': scope, + 'state': state, + + }) + + payload = await self._version.fetch_async(method='GET', uri=self._uri, headers=headers, params=params) return AuthorizeInstance(self._version, payload) + + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - return "" + return '' + diff --git a/twilio/rest/preview_iam/v1/token.py b/twilio/rest/preview_iam/v1/token.py index 461ee40fe..d6d7fc463 100644 --- a/twilio/rest/preview_iam/v1/token.py +++ b/twilio/rest/preview_iam/v1/token.py @@ -12,66 +12,70 @@ Do not edit the class manually. """ -from typing import Any, Dict, Optional, Union -from twilio.base import values + +from datetime import date, datetime +from decimal import Decimal +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource from twilio.base.version import Version + class TokenInstance(InstanceResource): + """ :ivar access_token: Token which carries the necessary information to access a Twilio resource directly. :ivar refresh_token: Token which carries the information necessary to get a new access token. :ivar id_token: Token which carries the information necessary of user profile. :ivar token_type: Token type - :ivar expires_in: + :ivar expires_in: """ def __init__(self, version: Version, payload: Dict[str, Any]): super().__init__(version) + self.access_token: Optional[str] = payload.get("access_token") self.refresh_token: Optional[str] = payload.get("refresh_token") self.id_token: Optional[str] = payload.get("id_token") self.token_type: Optional[str] = payload.get("token_type") self.expires_in: Optional[int] = payload.get("expires_in") + + + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ + + return '' - return "" -class TokenList(ListResource): +class TokenList(ListResource): + def __init__(self, version: Version): """ Initialize the TokenList :param version: Version that contains the resource - + """ super().__init__(version) - self._uri = "/token" - - def create( - self, - grant_type: str, - client_id: str, - client_secret: Union[str, object] = values.unset, - code: Union[str, object] = values.unset, - redirect_uri: Union[str, object] = values.unset, - audience: Union[str, object] = values.unset, - refresh_token: Union[str, object] = values.unset, - scope: Union[str, object] = values.unset, - ) -> TokenInstance: + + self._uri = '/token' + + + + def create(self, grant_type: str, client_id: str, client_secret: Union[str, object]=values.unset, code: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, audience: Union[str, object]=values.unset, refresh_token: Union[str, object]=values.unset, scope: Union[str, object]=values.unset) -> TokenInstance: """ Create the TokenInstance @@ -83,39 +87,30 @@ def create( :param audience: The targeted audience uri :param refresh_token: JWT token related to refresh access token. :param scope: The scope of token - + :returns: The created TokenInstance """ + + data = values.of({ + 'grant_type': grant_type, + 'client_id': client_id, + 'client_secret': client_secret, + 'code': code, + 'redirect_uri': redirect_uri, + 'audience': audience, + 'refresh_token': refresh_token, + 'scope': scope, + }) + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) - data = values.of( - { - "grant_type": grant_type, - "client_id": client_id, - "client_secret": client_secret, - "code": code, - "redirect_uri": redirect_uri, - "audience": audience, - "refresh_token": refresh_token, - "scope": scope, - } - ) - headers = values.of({"Content-Type": "application/x-www-form-urlencoded", "Requires-Authentication": "none"}) - payload = self._version.create( - method="POST", uri=self._uri, data=data, headers=headers - ) return TokenInstance(self._version, payload) - async def create_async( - self, - grant_type: str, - client_id: str, - client_secret: Union[str, object] = values.unset, - code: Union[str, object] = values.unset, - redirect_uri: Union[str, object] = values.unset, - audience: Union[str, object] = values.unset, - refresh_token: Union[str, object] = values.unset, - scope: Union[str, object] = values.unset, - ) -> TokenInstance: + async def create_async(self, grant_type: str, client_id: str, client_secret: Union[str, object]=values.unset, code: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, audience: Union[str, object]=values.unset, refresh_token: Union[str, object]=values.unset, scope: Union[str, object]=values.unset) -> TokenInstance: """ Asynchronously create the TokenInstance @@ -127,29 +122,31 @@ async def create_async( :param audience: The targeted audience uri :param refresh_token: JWT token related to refresh access token. :param scope: The scope of token - + :returns: The created TokenInstance """ - - data = values.of( - { - "grant_type": grant_type, - "client_id": client_id, - "client_secret": client_secret, - "code": code, - "redirect_uri": redirect_uri, - "audience": audience, - "refresh_token": refresh_token, - "scope": scope, - } - ) - headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) - - payload = await self._version.create_async( - method="POST", uri=self._uri, data=data, headers=headers - ) + + data = values.of({ + 'grant_type': grant_type, + 'client_id': client_id, + 'client_secret': client_secret, + 'code': code, + 'redirect_uri': redirect_uri, + 'audience': audience, + 'refresh_token': refresh_token, + 'scope': scope, + }) + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) return TokenInstance(self._version, payload) + + + def __repr__(self) -> str: """ @@ -157,4 +154,5 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return "" + return '' + diff --git a/twilio/rest/preview_iam/versionless/__init__.py b/twilio/rest/preview_iam/versionless/__init__.py new file mode 100644 index 000000000..946f389d9 --- /dev/null +++ b/twilio/rest/preview_iam/versionless/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Organization Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview_iam.versionless.organization import OrganizationList + + +class Versionless(Version): + + def __init__(self, domain: Domain): + """ + Initialize the Versionless version of PreviewIam + + :param domain: The Twilio.preview_iam domain + """ + super().__init__(domain, "Organizations") + self._organization: Optional[OrganizationList] = None + + @property + def organization(self) -> OrganizationList: + if self._organization is None: + self._organization = OrganizationList(self) + return self._organization + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/twilio/rest/preview_iam/versionless/organization/__init__.py b/twilio/rest/preview_iam/versionless/organization/__init__.py new file mode 100644 index 000000000..00b99a9a2 --- /dev/null +++ b/twilio/rest/preview_iam/versionless/organization/__init__.py @@ -0,0 +1,147 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Organization Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + + +from datetime import date, datetime +from decimal import Decimal +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +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.versionless.organization.user import UserList + + + +class OrganizationContext(InstanceContext): + + def __init__(self, version: Version, organization_sid: str): + """ + Initialize the OrganizationContext + + :param version: Version that contains the resource + :param organization_sid: + """ + super().__init__(version) + + + # Path Solution + self._solution = { + 'organization_sid': organization_sid, + } + self._uri = '/{organization_sid}'.format(**self._solution) + + self._accounts: Optional[AccountList] = None + self._role_assignments: Optional[RoleAssignmentList] = None + self._users: Optional[UserList] = None + + + + @property + def accounts(self) -> AccountList: + """ + Access the accounts + """ + if self._accounts is None: + self._accounts = AccountList( + self._version, + self._solution['organization_sid'], + ) + return self._accounts + + @property + def role_assignments(self) -> RoleAssignmentList: + """ + Access the role_assignments + """ + if self._role_assignments is None: + self._role_assignments = RoleAssignmentList( + self._version, + self._solution['organization_sid'], + ) + return self._role_assignments + + @property + def users(self) -> UserList: + """ + Access the users + """ + if self._users is None: + self._users = UserList( + self._version, + self._solution['organization_sid'], + ) + return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + + + +class OrganizationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the OrganizationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + + + + + + + + + + + + def get(self, organization_sid: str) -> OrganizationContext: + """ + Constructs a OrganizationContext + + :param organization_sid: + """ + return OrganizationContext(self._version, organization_sid=organization_sid) + + def __call__(self, organization_sid: str) -> OrganizationContext: + """ + Constructs a OrganizationContext + + :param organization_sid: + """ + return OrganizationContext(self._version, organization_sid=organization_sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return '' + diff --git a/twilio/rest/preview_iam/organizations/account.py b/twilio/rest/preview_iam/versionless/organization/account.py similarity index 70% rename from twilio/rest/preview_iam/organizations/account.py rename to twilio/rest/preview_iam/versionless/organization/account.py index 1b92f6641..ec5c524c3 100644 --- a/twilio/rest/preview_iam/organizations/account.py +++ b/twilio/rest/preview_iam/versionless/organization/account.py @@ -12,9 +12,11 @@ Do not edit the class manually. """ -from datetime import datetime + +from datetime import date, datetime +from decimal import Decimal from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import deserialize, values +from twilio.base import deserialize, serialize, values from twilio.base.instance_context import InstanceContext from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource @@ -23,6 +25,8 @@ class AccountInstance(InstanceResource): + + """ :ivar account_sid: Twilio account sid :ivar friendly_name: Account friendly name @@ -31,25 +35,19 @@ class AccountInstance(InstanceResource): :ivar date_created: The date and time when the account was created in the system """ - def __init__( - self, - version: Version, - payload: Dict[str, Any], - organization_sid: Optional[str] = None, - account_sid: Optional[str] = None, - ): + def __init__(self, version: Version, payload: Dict[str, Any], organization_sid: str, account_sid: Optional[str] = None): super().__init__(version) + self.account_sid: Optional[str] = payload.get("account_sid") self.friendly_name: Optional[str] = payload.get("friendly_name") self.status: Optional[str] = payload.get("status") self.owner_sid: Optional[str] = payload.get("owner_sid") - self.date_created: Optional[datetime] = deserialize.iso8601_datetime( - payload.get("date_created") - ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime(payload.get("date_created")) - self._solution = { - "organization_sid": organization_sid or self.organization_sid, + + self._solution = { + "organization_sid": organization_sid, "account_sid": account_sid or self.account_sid, } self._context: Optional[AccountContext] = None @@ -63,17 +61,14 @@ def _proxy(self) -> "AccountContext": :returns: AccountContext for this AccountInstance """ if self._context is None: - self._context = AccountContext( - self._version, - organization_sid=self._solution["organization_sid"], - account_sid=self._solution["account_sid"], - ) + self._context = AccountContext(self._version, organization_sid=self._solution['organization_sid'], account_sid=self._solution['account_sid'],) return self._context - + + def fetch(self) -> "AccountInstance": """ Fetch the AccountInstance - + :returns: The fetched AccountInstance """ @@ -82,21 +77,20 @@ def fetch(self) -> "AccountInstance": async def fetch_async(self) -> "AccountInstance": """ Asynchronous coroutine to fetch the AccountInstance - + :returns: The fetched AccountInstance """ return await self._proxy.fetch_async() - + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) - return "".format(context) - + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) class AccountContext(InstanceContext): @@ -105,68 +99,81 @@ def __init__(self, version: Version, organization_sid: str, account_sid: str): Initialize the AccountContext :param version: Version that contains the resource - :param organization_sid: - :param account_sid: + :param organization_sid: + :param account_sid: """ super().__init__(version) + # Path Solution - self._solution = { - "organization_sid": organization_sid, - "account_sid": account_sid, + self._solution = { + 'organization_sid': organization_sid, + 'account_sid': account_sid, } - self._uri = "/{organization_sid}/Accounts/{account_sid}".format( - **self._solution - ) - + self._uri = '/{organization_sid}/Accounts/{account_sid}'.format(**self._solution) + + + def fetch(self) -> AccountInstance: """ - Fetch the AccountInstance - + Fetch1 the AccountInstance + :returns: The fetched AccountInstance """ + - payload = self._version.fetch( - method="GET", - uri=self._uri, - ) + headers = values.of({}) + + + headers["Accept"] = "application/json" + + payload = self._version.fetch(method='GET', uri=self._uri , headers=headers) return AccountInstance( self._version, payload, - organization_sid=self._solution["organization_sid"], - account_sid=self._solution["account_sid"], + organization_sid=self._solution['organization_sid'], + account_sid=self._solution['account_sid'], + ) async def fetch_async(self) -> AccountInstance: """ Asynchronous coroutine to fetch the AccountInstance - + :returns: The fetched AccountInstance """ + - payload = await self._version.fetch_async( - method="GET", - uri=self._uri, - ) + headers = values.of({}) + + + headers["Accept"] = "application/json" + + payload = await self._version.fetch_async(method='GET', uri=self._uri , headers=headers) return AccountInstance( self._version, payload, - organization_sid=self._solution["organization_sid"], - account_sid=self._solution["account_sid"], + organization_sid=self._solution['organization_sid'], + account_sid=self._solution['account_sid'], + ) - + + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) - return "".format(context) + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + + + class AccountPage(Page): @@ -177,9 +184,7 @@ def get_instance(self, payload: Dict[str, Any]) -> AccountInstance: :param payload: Payload response from the API """ - return AccountInstance( - self._version, payload, organization_sid=self._solution["organization_sid"] - ) + return AccountInstance(self._version, payload, organization_sid=self._solution["organization_sid"]) def __repr__(self) -> str: """ @@ -187,29 +192,34 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return "" + return "" -class AccountList(ListResource): + + +class AccountList(ListResource): + def __init__(self, version: Version, organization_sid: str): """ Initialize the AccountList :param version: Version that contains the resource - :param organization_sid: - + :param organization_sid: + """ super().__init__(version) + # Path Solution - self._solution = { - "organization_sid": organization_sid, - } - self._uri = "/{organization_sid}/Accounts".format(**self._solution) - - def stream( - self, + self._solution = { 'organization_sid': organization_sid, } + self._uri = '/{organization_sid}/Accounts'.format(**self._solution) + + + + + def stream(self, + limit: Optional[int] = None, page_size: Optional[int] = None, ) -> Iterator[AccountInstance]: @@ -218,7 +228,7 @@ def stream( This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - + :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -229,12 +239,14 @@ def stream( :returns: Generator that will yield up to limit results """ limits = self._version.read_limits(limit, page_size) - page = self.page(page_size=limits["page_size"]) + page = self.page( + page_size=limits['page_size'] + ) - return self._version.stream(page, limits["limit"]) + return self._version.stream(page, limits['limit']) - async def stream_async( - self, + async def stream_async(self, + limit: Optional[int] = None, page_size: Optional[int] = None, ) -> AsyncIterator[AccountInstance]: @@ -243,7 +255,7 @@ async def stream_async( This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - + :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -254,12 +266,14 @@ async def stream_async( :returns: Generator that will yield up to limit results """ limits = self._version.read_limits(limit, page_size) - page = await self.page_async(page_size=limits["page_size"]) + page = await self.page_async( + page_size=limits['page_size'] + ) - return self._version.stream_async(page, limits["limit"]) + return self._version.stream_async(page, limits['limit']) - def list( - self, + def list(self, + limit: Optional[int] = None, page_size: Optional[int] = None, ) -> List[AccountInstance]: @@ -267,7 +281,7 @@ def list( Lists AccountInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - + :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -277,15 +291,13 @@ def list( :returns: list that will contain up to limit results """ - return list( - self.stream( - limit=limit, - page_size=page_size, - ) - ) + return list(self.stream( + limit=limit, + page_size=page_size, + )) - async def list_async( - self, + async def list_async(self, + limit: Optional[int] = None, page_size: Optional[int] = None, ) -> List[AccountInstance]: @@ -293,7 +305,7 @@ async def list_async( Asynchronously lists AccountInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - + :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -303,16 +315,13 @@ async def list_async( :returns: list that will contain up to limit results """ - return [ - record - async for record in await self.stream_async( - limit=limit, - page_size=page_size, - ) - ] + return [record async for record in await self.stream_async( + limit=limit, + page_size=page_size, + )] - def page( - self, + def page(self, + page_token: Union[str, object] = values.unset, page_number: Union[int, object] = values.unset, page_size: Union[int, object] = values.unset, @@ -320,26 +329,32 @@ def page( """ Retrieve a single page of AccountInstance records from the API. Request is executed immediately - + :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of AccountInstance """ - data = values.of( - { - "PageToken": page_token, - "Page": page_number, - "PageSize": page_size, - } - ) - - response = self._version.page(method="GET", uri=self._uri, params=data) + data = values.of({ + 'PageToken': page_token, + 'Page': page_number, + 'PageSize': page_size, + }) + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + headers["Accept"] = "application/json" + + + response = self._version.page(method='GET', uri=self._uri, params=data, headers=headers) return AccountPage(self._version, response, self._solution) - async def page_async( - self, + async def page_async(self, + page_token: Union[str, object] = values.unset, page_number: Union[int, object] = values.unset, page_size: Union[int, object] = values.unset, @@ -347,24 +362,28 @@ async def page_async( """ Asynchronously retrieve a single page of AccountInstance records from the API. Request is executed immediately - + :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of AccountInstance """ - data = values.of( - { - "PageToken": page_token, - "Page": page_number, - "PageSize": page_size, - } - ) - - response = await self._version.page_async( - method="GET", uri=self._uri, params=data - ) + data = values.of({ + 'PageToken': page_token, + 'Page': page_number, + 'PageSize': page_size, + }) + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + headers["Accept"] = "application/json" + + + response = await self._version.page_async(method='GET', uri=self._uri, params=data, headers=headers) return AccountPage(self._version, response, self._solution) def get_page(self, target_url: str) -> AccountPage: @@ -376,7 +395,10 @@ def get_page(self, target_url: str) -> AccountPage: :returns: Page of AccountInstance """ - response = self._version.domain.twilio.request("GET", target_url) + response = self._version.domain.twilio.request( + 'GET', + target_url + ) return AccountPage(self._version, response, self._solution) async def get_page_async(self, target_url: str) -> AccountPage: @@ -388,30 +410,29 @@ async def get_page_async(self, target_url: str) -> AccountPage: :returns: Page of AccountInstance """ - response = await self._version.domain.twilio.request_async("GET", target_url) + response = await self._version.domain.twilio.request_async( + 'GET', + target_url + ) return AccountPage(self._version, response, self._solution) - def get(self, organization_sid: str, account_sid: str) -> AccountContext: + + + def get(self, account_sid: str) -> AccountContext: """ Constructs a AccountContext - - :param organization_sid: - :param account_sid: + + :param account_sid: """ - return AccountContext( - self._version, organization_sid=organization_sid, account_sid=account_sid - ) + return AccountContext(self._version, organization_sid=self._solution['organization_sid'], account_sid=account_sid) - def __call__(self, organization_sid: str, account_sid: str) -> AccountContext: + def __call__(self, account_sid: str) -> AccountContext: """ Constructs a AccountContext - - :param organization_sid: - :param account_sid: + + :param account_sid: """ - return AccountContext( - self._version, organization_sid=organization_sid, account_sid=account_sid - ) + return AccountContext(self._version, organization_sid=self._solution['organization_sid'], account_sid=account_sid) def __repr__(self) -> str: """ @@ -419,4 +440,5 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return "" + return '' + diff --git a/twilio/rest/preview_iam/organizations/role_assignment.py b/twilio/rest/preview_iam/versionless/organization/role_assignment.py similarity index 60% rename from twilio/rest/preview_iam/organizations/role_assignment.py rename to twilio/rest/preview_iam/versionless/organization/role_assignment.py index 03f9b26ee..ec6e932e2 100644 --- a/twilio/rest/preview_iam/organizations/role_assignment.py +++ b/twilio/rest/preview_iam/versionless/organization/role_assignment.py @@ -12,8 +12,11 @@ Do not edit the class manually. """ + +from datetime import date, datetime +from decimal import Decimal from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import values +from twilio.base import deserialize, serialize, values from twilio.base.instance_context import InstanceContext from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource @@ -22,6 +25,31 @@ class RoleAssignmentInstance(InstanceResource): + + class PublicApiCreateRoleAssignmentRequest(object): + """ + :ivar role_sid: Twilio Role Sid representing assigned role + :ivar scope: Twilio Sid representing scope of this assignment + :ivar identity: Twilio Sid representing identity of this assignment + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.role_sid: Optional[str] = payload.get("role_sid") + self.scope: Optional[str] = payload.get("scope") + self.identity: Optional[str] = payload.get("identity") + + def to_dict(self): + return { + + "role_sid": self.role_sid, + "scope": self.scope, + "identity": self.identity, + } + + + """ :ivar sid: Twilio Role Assignment Sid representing this role assignment :ivar role_sid: Twilio Role Sid representing assigned role @@ -33,15 +61,10 @@ class RoleAssignmentInstance(InstanceResource): :ivar status: HTTP response status code """ - def __init__( - self, - version: Version, - payload: Dict[str, Any], - organization_sid: Optional[str] = None, - role_assignment_sid: Optional[str] = None, - ): + def __init__(self, version: Version, payload: Dict[str, Any], organization_sid: str, sid: Optional[str] = None): super().__init__(version) + self.sid: Optional[str] = payload.get("sid") self.role_sid: Optional[str] = payload.get("role_sid") self.scope: Optional[str] = payload.get("scope") @@ -51,9 +74,10 @@ def __init__( self.more_info: Optional[str] = payload.get("moreInfo") self.status: Optional[int] = payload.get("status") - self._solution = { - "organization_sid": organization_sid or self.organization_sid, - "role_assignment_sid": role_assignment_sid or self.role_assignment_sid, + + self._solution = { + "organization_sid": organization_sid, + "sid": sid or self.sid, } self._context: Optional[RoleAssignmentContext] = None @@ -66,100 +90,128 @@ def _proxy(self) -> "RoleAssignmentContext": :returns: RoleAssignmentContext for this RoleAssignmentInstance """ if self._context is None: - self._context = RoleAssignmentContext( - self._version, - organization_sid=self._solution["organization_sid"], - role_assignment_sid=self._solution["role_assignment_sid"], - ) + self._context = RoleAssignmentContext(self._version, organization_sid=self._solution['organization_sid'], sid=self._solution['sid'],) return self._context - + + def delete(self) -> bool: """ Deletes the RoleAssignmentInstance - + :returns: True if delete succeeds, False otherwise """ return self._proxy.delete() - async def delete_async(self) -> bool: """ Asynchronous coroutine that deletes the RoleAssignmentInstance - + :returns: True if delete succeeds, False otherwise """ return await self._proxy.delete_async() - + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) - return "".format( - context - ) - + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) class RoleAssignmentContext(InstanceContext): - def __init__( - self, version: Version, organization_sid: str, role_assignment_sid: str - ): + class PublicApiCreateRoleAssignmentRequest(object): + """ + :ivar role_sid: Twilio Role Sid representing assigned role + :ivar scope: Twilio Sid representing scope of this assignment + :ivar identity: Twilio Sid representing identity of this assignment + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.role_sid: Optional[str] = payload.get("role_sid") + self.scope: Optional[str] = payload.get("scope") + self.identity: Optional[str] = payload.get("identity") + + def to_dict(self): + return { + + "role_sid": self.role_sid, + "scope": self.scope, + "identity": self.identity, + } + + + def __init__(self, version: Version, organization_sid: str, sid: str): """ Initialize the RoleAssignmentContext :param version: Version that contains the resource - :param organization_sid: - :param role_assignment_sid: + :param organization_sid: + :param sid: """ super().__init__(version) + # Path Solution - self._solution = { - "organization_sid": organization_sid, - "role_assignment_sid": role_assignment_sid, + self._solution = { + 'organization_sid': organization_sid, + 'sid': sid, } - self._uri = "/{organization_sid}/RoleAssignments/{role_assignment_sid}".format( - **self._solution - ) - + self._uri = '/{organization_sid}/RoleAssignments/{sid}'.format(**self._solution) + + + def delete(self) -> bool: """ Deletes the RoleAssignmentInstance - + :returns: True if delete succeeds, False otherwise """ - return self._version.delete( - method="DELETE", - uri=self._uri, - ) + + + headers = values.of({}) + + + + headers["Accept"] = "application/scim+json" + + return self._version.delete(method='DELETE', uri=self._uri, headers=headers) async def delete_async(self) -> bool: """ Asynchronous coroutine that deletes the RoleAssignmentInstance - + :returns: True if delete succeeds, False otherwise """ - return await self._version.delete_async( - method="DELETE", - uri=self._uri, - ) - + + headers = values.of({}) + + + + headers["Accept"] = "application/scim+json" + + return await self._version.delete_async(method='DELETE', uri=self._uri, headers=headers) + + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) - return "".format( - context - ) + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + + + + + class RoleAssignmentPage(Page): @@ -170,9 +222,7 @@ def get_instance(self, payload: Dict[str, Any]) -> RoleAssignmentInstance: :param payload: Payload response from the API """ - return RoleAssignmentInstance( - self._version, payload, organization_sid=self._solution["organization_sid"] - ) + return RoleAssignmentInstance(self._version, payload, organization_sid=self._solution["organization_sid"]) def __repr__(self) -> str: """ @@ -180,97 +230,110 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return "" + return "" + -class RoleAssignmentList(ListResource): + +class RoleAssignmentList(ListResource): + class PublicApiCreateRoleAssignmentRequest(object): """ - :ivar role_sid: Twilio Role Sid representing assigned role - :ivar scope: Twilio Sid representing scope of this assignment - :ivar identity: Twilio Sid representing identity of this assignment + :ivar role_sid: Twilio Role Sid representing assigned role + :ivar scope: Twilio Sid representing scope of this assignment + :ivar identity: Twilio Sid representing identity of this assignment """ def __init__(self, payload: Dict[str, Any]): + self.role_sid: Optional[str] = payload.get("role_sid") self.scope: Optional[str] = payload.get("scope") self.identity: Optional[str] = payload.get("identity") def to_dict(self): return { - "role_sid": self.role_sid, - "scope": self.scope, - "identity": self.identity, + + "role_sid": self.role_sid, + "scope": self.scope, + "identity": self.identity, } + def __init__(self, version: Version, organization_sid: str): """ Initialize the RoleAssignmentList :param version: Version that contains the resource - :param organization_sid: - + :param organization_sid: + """ super().__init__(version) + # Path Solution - self._solution = { - "organization_sid": organization_sid, - } - self._uri = "/{organization_sid}/RoleAssignments".format(**self._solution) - - def create( - self, - public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest, - ) -> RoleAssignmentInstance: + self._solution = { 'organization_sid': organization_sid, } + self._uri = '/{organization_sid}/RoleAssignments'.format(**self._solution) + + + + + def create(self, public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest) -> RoleAssignmentInstance: """ Create the RoleAssignmentInstance - :param public_api_create_role_assignment_request: - + :param public_api_create_role_assignment_request: + :returns: The created RoleAssignmentInstance """ data = public_api_create_role_assignment_request.to_dict() - - headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + headers["Content-Type"] = "application/json" + + + headers["Accept"] = "application/json" + + headers["Accept"] = "application/scim+json" + + + payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) - payload = self._version.create( - method="POST", uri=self._uri, data=data, headers=headers - ) + return RoleAssignmentInstance(self._version, payload, organization_sid=self._solution['organization_sid']) - return RoleAssignmentInstance( - self._version, payload, organization_sid=self._solution["organization_sid"] - ) - - async def create_async( - self, - public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest, - ) -> RoleAssignmentInstance: + async def create_async(self, public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest) -> RoleAssignmentInstance: """ Asynchronously create the RoleAssignmentInstance - :param public_api_create_role_assignment_request: - + :param public_api_create_role_assignment_request: + :returns: The created RoleAssignmentInstance """ data = public_api_create_role_assignment_request.to_dict() - - headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + headers["Content-Type"] = "application/json" - - payload = await self._version.create_async( - method="POST", uri=self._uri, data=data, headers=headers - ) - - return RoleAssignmentInstance( - self._version, payload, organization_sid=self._solution["organization_sid"] - ) - - def stream( - self, + + + headers["Accept"] = "application/json" + + headers["Accept"] = "application/scim+json" + + + payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) + + return RoleAssignmentInstance(self._version, payload, organization_sid=self._solution['organization_sid']) + + + def stream(self, + identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, limit: Optional[int] = None, @@ -281,9 +344,9 @@ def stream( This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - - :param str identity: - :param str scope: + + :param str identity: + :param str scope: :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -294,12 +357,16 @@ def stream( :returns: Generator that will yield up to limit results """ limits = self._version.read_limits(limit, page_size) - page = self.page(identity=identity, scope=scope, page_size=limits["page_size"]) + page = self.page( + identity=identity, + scope=scope, + page_size=limits['page_size'] + ) - return self._version.stream(page, limits["limit"]) + return self._version.stream(page, limits['limit']) - async def stream_async( - self, + async def stream_async(self, + identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, limit: Optional[int] = None, @@ -310,9 +377,9 @@ async def stream_async( This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - - :param str identity: - :param str scope: + + :param str identity: + :param str scope: :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -324,13 +391,15 @@ async def stream_async( """ limits = self._version.read_limits(limit, page_size) page = await self.page_async( - identity=identity, scope=scope, page_size=limits["page_size"] + identity=identity, + scope=scope, + page_size=limits['page_size'] ) - return self._version.stream_async(page, limits["limit"]) + return self._version.stream_async(page, limits['limit']) - def list( - self, + def list(self, + identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, limit: Optional[int] = None, @@ -340,9 +409,9 @@ def list( Lists RoleAssignmentInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - - :param str identity: - :param str scope: + + :param str identity: + :param str scope: :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -352,17 +421,15 @@ def list( :returns: list that will contain up to limit results """ - return list( - self.stream( - identity=identity, - scope=scope, - limit=limit, - page_size=page_size, - ) - ) + return list(self.stream( + identity=identity, + scope=scope, + limit=limit, + page_size=page_size, + )) - async def list_async( - self, + async def list_async(self, + identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, limit: Optional[int] = None, @@ -372,9 +439,9 @@ async def list_async( Asynchronously lists RoleAssignmentInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - - :param str identity: - :param str scope: + + :param str identity: + :param str scope: :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -384,18 +451,15 @@ async def list_async( :returns: list that will contain up to limit results """ - return [ - record - async for record in await self.stream_async( - identity=identity, - scope=scope, - limit=limit, - page_size=page_size, - ) - ] - - def page( - self, + return [record async for record in await self.stream_async( + identity=identity, + scope=scope, + limit=limit, + page_size=page_size, + )] + + def page(self, + identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, page_token: Union[str, object] = values.unset, @@ -405,30 +469,36 @@ def page( """ Retrieve a single page of RoleAssignmentInstance records from the API. Request is executed immediately - - :param identity: - :param scope: + + :param identity: + :param scope: :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of RoleAssignmentInstance """ - data = values.of( - { - "Identity": identity, - "Scope": scope, - "PageToken": page_token, - "Page": page_number, - "PageSize": page_size, - } - ) - - response = self._version.page(method="GET", uri=self._uri, params=data) + data = values.of({ + 'Identity': identity, + 'Scope': scope, + 'PageToken': page_token, + 'Page': page_number, + 'PageSize': page_size, + }) + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + headers["Accept"] = "application/json" + + + response = self._version.page(method='GET', uri=self._uri, params=data, headers=headers) return RoleAssignmentPage(self._version, response, self._solution) - async def page_async( - self, + async def page_async(self, + identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, page_token: Union[str, object] = values.unset, @@ -438,28 +508,32 @@ async def page_async( """ Asynchronously retrieve a single page of RoleAssignmentInstance records from the API. Request is executed immediately - - :param identity: - :param scope: + + :param identity: + :param scope: :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of RoleAssignmentInstance """ - data = values.of( - { - "Identity": identity, - "Scope": scope, - "PageToken": page_token, - "Page": page_number, - "PageSize": page_size, - } - ) - - response = await self._version.page_async( - method="GET", uri=self._uri, params=data - ) + data = values.of({ + 'Identity': identity, + 'Scope': scope, + 'PageToken': page_token, + 'Page': page_number, + 'PageSize': page_size, + }) + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + headers["Accept"] = "application/json" + + + response = await self._version.page_async(method='GET', uri=self._uri, params=data, headers=headers) return RoleAssignmentPage(self._version, response, self._solution) def get_page(self, target_url: str) -> RoleAssignmentPage: @@ -471,7 +545,10 @@ def get_page(self, target_url: str) -> RoleAssignmentPage: :returns: Page of RoleAssignmentInstance """ - response = self._version.domain.twilio.request("GET", target_url) + response = self._version.domain.twilio.request( + 'GET', + target_url + ) return RoleAssignmentPage(self._version, response, self._solution) async def get_page_async(self, target_url: str) -> RoleAssignmentPage: @@ -483,38 +560,29 @@ async def get_page_async(self, target_url: str) -> RoleAssignmentPage: :returns: Page of RoleAssignmentInstance """ - response = await self._version.domain.twilio.request_async("GET", target_url) + response = await self._version.domain.twilio.request_async( + 'GET', + target_url + ) return RoleAssignmentPage(self._version, response, self._solution) - def get( - self, organization_sid: str, role_assignment_sid: str - ) -> RoleAssignmentContext: + + + def get(self, sid: str) -> RoleAssignmentContext: """ Constructs a RoleAssignmentContext - - :param organization_sid: - :param role_assignment_sid: + + :param sid: """ - return RoleAssignmentContext( - self._version, - organization_sid=organization_sid, - role_assignment_sid=role_assignment_sid, - ) + return RoleAssignmentContext(self._version, organization_sid=self._solution['organization_sid'], sid=sid) - def __call__( - self, organization_sid: str, role_assignment_sid: str - ) -> RoleAssignmentContext: + def __call__(self, sid: str) -> RoleAssignmentContext: """ Constructs a RoleAssignmentContext - - :param organization_sid: - :param role_assignment_sid: + + :param sid: """ - return RoleAssignmentContext( - self._version, - organization_sid=organization_sid, - role_assignment_sid=role_assignment_sid, - ) + return RoleAssignmentContext(self._version, organization_sid=self._solution['organization_sid'], sid=sid) def __repr__(self) -> str: """ @@ -522,4 +590,5 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return "" + return '' + diff --git a/twilio/rest/preview_iam/versionless/organization/user.py b/twilio/rest/preview_iam/versionless/organization/user.py new file mode 100644 index 000000000..54cd2edd9 --- /dev/null +++ b/twilio/rest/preview_iam/versionless/organization/user.py @@ -0,0 +1,1068 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Organization Public API + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + + +from datetime import date, datetime +from decimal import Decimal +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserInstance(InstanceResource): + + class ScimEmailAddress(object): + """ + :ivar primary: Indicates if this email address is the primary one + :ivar value: The actual email address value + :ivar type: The type of email address (e.g., work, home, etc.) + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.primary: Optional[bool] = payload.get("primary") + self.value: Optional[str] = payload.get("value") + self.type: Optional[str] = payload.get("type") + + def to_dict(self): + return { + + "primary": self.primary, + "value": self.value, + "type": self.type, + } + + class ScimMeta(object): + """ + :ivar resource_type: Indicates the type of the resource + :ivar created: The date and time when the resource was created in the system + :ivar last_modified: The date and time when the resource was last modified + :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.resource_type: Optional[str] = payload.get("resource_type") + self.created: Optional[datetime] = payload.get("created") + self.last_modified: Optional[datetime] = payload.get("last_modified") + self.version: Optional[str] = payload.get("version") + + def to_dict(self): + return { + + "resource_type": self.resource_type, + "created": self.created, + "last_modified": self.last_modified, + "version": self.version, + } + + class ScimName(object): + """ + :ivar given_name: The user's first or given name + :ivar family_name: The user's last or family name + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.given_name: Optional[str] = payload.get("given_name") + self.family_name: Optional[str] = payload.get("family_name") + + def to_dict(self): + return { + + "given_name": self.given_name, + "family_name": self.family_name, + } + + class ScimUser(object): + """ + :ivar id: Unique Twilio user sid + :ivar external_id: External unique resource id defined by provisioning client + :ivar user_name: Unique username, MUST be same as primary email address + :ivar display_name: User friendly display name + :ivar name: + :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + :ivar active: Indicates whether the user is active + :ivar locale: User's locale + :ivar timezone: User's time zone + :ivar schemas: An array of URIs that indicate the schemas supported for this user resource + :ivar meta: + :ivar detail: A human-readable description of the error + :ivar scim_type: A scimType error code as defined in RFC7644 + :ivar status: Http status code + :ivar code: Twilio-specific error code + :ivar more_info: Link to Error Code References + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.id: Optional[str] = payload.get("id") + self.external_id: Optional[str] = payload.get("external_id") + self.user_name: Optional[str] = payload.get("user_name") + self.display_name: Optional[str] = payload.get("display_name") + self.name: Optional[UserList.ScimName] = payload.get("name") + self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get("emails") + self.active: Optional[bool] = payload.get("active") + self.locale: Optional[str] = payload.get("locale") + self.timezone: Optional[str] = payload.get("timezone") + self.schemas: Optional[List[str]] = payload.get("schemas") + self.meta: Optional[UserList.ScimMeta] = payload.get("meta") + self.detail: Optional[str] = payload.get("detail") + self.scim_type: Optional[str] = payload.get("scim_type") + self.status: Optional[str] = payload.get("status") + self.code: Optional[int] = payload.get("code") + self.more_info: Optional[str] = payload.get("more_info") + + def to_dict(self): + return { + + "id": self.id, + "externalId": self.external_id, + "userName": self.user_name, + "displayName": self.display_name, + "name": self.name.to_dict() if self.name is not None else None , + "emails": [emails.to_dict() for emails in self.emails] if self.emails is not None else None, + "active": self.active, + "locale": self.locale, + "timezone": self.timezone, + "schemas": self.schemas, + "meta": self.meta.to_dict() if self.meta is not None else None , + "detail": self.detail, + "scimType": self.scim_type, + "status": self.status, + "code": self.code, + "moreInfo": self.more_info, + } + + + + """ + :ivar id: Unique Twilio user sid + :ivar external_id: External unique resource id defined by provisioning client + :ivar user_name: Unique username, MUST be same as primary email address + :ivar display_name: User friendly display name + :ivar name: + :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + :ivar active: Indicates whether the user is active + :ivar locale: User's locale + :ivar timezone: User's time zone + :ivar schemas: An array of URIs that indicate the schemas supported for this user resource + :ivar meta: + :ivar detail: A human-readable description of the error + :ivar scim_type: A scimType error code as defined in RFC7644 + :ivar status: Http status code + :ivar code: Twilio-specific error code + :ivar more_info: Link to Error Code References + """ + + def __init__(self, version: Version, payload: Dict[str, Any], organization_sid: str, id: Optional[str] = None): + super().__init__(version) + + + self.id: Optional[str] = payload.get("id") + self.external_id: Optional[str] = payload.get("externalId") + self.user_name: Optional[str] = payload.get("userName") + self.display_name: Optional[str] = payload.get("displayName") + self.name: Optional[UserList.str] = payload.get("name") + self.emails: Optional[List[UserList.str]] = payload.get("emails") + self.active: Optional[bool] = payload.get("active") + self.locale: Optional[str] = payload.get("locale") + self.timezone: Optional[str] = payload.get("timezone") + self.schemas: Optional[List[str]] = payload.get("schemas") + self.meta: Optional[UserList.str] = payload.get("meta") + self.detail: Optional[str] = payload.get("detail") + self.scim_type: Optional[str] = payload.get("scimType") + self.status: Optional[str] = payload.get("status") + self.code: Optional[int] = payload.get("code") + self.more_info: Optional[str] = payload.get("moreInfo") + + + self._solution = { + "organization_sid": organization_sid, + "id": id or self.id, + } + self._context: Optional[UserContext] = None + + @property + def _proxy(self) -> "UserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserContext for this UserInstance + """ + if self._context is None: + self._context = UserContext(self._version, organization_sid=self._solution['organization_sid'], id=self._solution['id'],) + return self._context + + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + + def fetch(self) -> "UserInstance": + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserInstance": + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return await self._proxy.fetch_async() + + + def update(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> "UserInstance": + """hello3 + Update the UserInstance + + :param scim_user: + :param if_match: + + :returns: The updated UserInstance + """ + return self._proxy.update(scim_user=scim_user, if_match=if_match, ) + + async def update_async(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> "UserInstance": + """ + Asynchronous coroutine to update the UserInstance + + :param scim_user: + :param if_match: + + :returns: The updated UserInstance + """ + return await self._proxy.update_async(scim_user=scim_user, if_match=if_match, ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + +class UserContext(InstanceContext): + + class ScimEmailAddress(object): + """ + :ivar primary: Indicates if this email address is the primary one + :ivar value: The actual email address value + :ivar type: The type of email address (e.g., work, home, etc.) + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.primary: Optional[bool] = payload.get("primary") + self.value: Optional[str] = payload.get("value") + self.type: Optional[str] = payload.get("type") + + def to_dict(self): + return { + + "primary": self.primary, + "value": self.value, + "type": self.type, + } + + class ScimMeta(object): + """ + :ivar resource_type: Indicates the type of the resource + :ivar created: The date and time when the resource was created in the system + :ivar last_modified: The date and time when the resource was last modified + :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.resource_type: Optional[str] = payload.get("resource_type") + self.created: Optional[datetime] = payload.get("created") + self.last_modified: Optional[datetime] = payload.get("last_modified") + self.version: Optional[str] = payload.get("version") + + def to_dict(self): + return { + + "resource_type": self.resource_type, + "created": self.created, + "last_modified": self.last_modified, + "version": self.version, + } + + class ScimName(object): + """ + :ivar given_name: The user's first or given name + :ivar family_name: The user's last or family name + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.given_name: Optional[str] = payload.get("given_name") + self.family_name: Optional[str] = payload.get("family_name") + + def to_dict(self): + return { + + "given_name": self.given_name, + "family_name": self.family_name, + } + + class ScimUser(object): + """ + :ivar id: Unique Twilio user sid + :ivar external_id: External unique resource id defined by provisioning client + :ivar user_name: Unique username, MUST be same as primary email address + :ivar display_name: User friendly display name + :ivar name: + :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + :ivar active: Indicates whether the user is active + :ivar locale: User's locale + :ivar timezone: User's time zone + :ivar schemas: An array of URIs that indicate the schemas supported for this user resource + :ivar meta: + :ivar detail: A human-readable description of the error + :ivar scim_type: A scimType error code as defined in RFC7644 + :ivar status: Http status code + :ivar code: Twilio-specific error code + :ivar more_info: Link to Error Code References + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.id: Optional[str] = payload.get("id") + self.external_id: Optional[str] = payload.get("external_id") + self.user_name: Optional[str] = payload.get("user_name") + self.display_name: Optional[str] = payload.get("display_name") + self.name: Optional[UserList.ScimName] = payload.get("name") + self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get("emails") + self.active: Optional[bool] = payload.get("active") + self.locale: Optional[str] = payload.get("locale") + self.timezone: Optional[str] = payload.get("timezone") + self.schemas: Optional[List[str]] = payload.get("schemas") + self.meta: Optional[UserList.ScimMeta] = payload.get("meta") + self.detail: Optional[str] = payload.get("detail") + self.scim_type: Optional[str] = payload.get("scim_type") + self.status: Optional[str] = payload.get("status") + self.code: Optional[int] = payload.get("code") + self.more_info: Optional[str] = payload.get("more_info") + + def to_dict(self): + return { + + "id": self.id, + "externalId": self.external_id, + "userName": self.user_name, + "displayName": self.display_name, + "name": self.name.to_dict() if self.name is not None else None , + "emails": [emails.to_dict() for emails in self.emails] if self.emails is not None else None, + "active": self.active, + "locale": self.locale, + "timezone": self.timezone, + "schemas": self.schemas, + "meta": self.meta.to_dict() if self.meta is not None else None , + "detail": self.detail, + "scimType": self.scim_type, + "status": self.status, + "code": self.code, + "moreInfo": self.more_info, + } + + + def __init__(self, version: Version, organization_sid: str, id: str): + """ + Initialize the UserContext + + :param version: Version that contains the resource + :param organization_sid: + :param id: + """ + super().__init__(version) + + + # Path Solution + self._solution = { + 'organization_sid': organization_sid, + 'id': id, + } + self._uri = '/{organization_sid}/scim/Users/{id}'.format(**self._solution) + + + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + + + headers = values.of({}) + + + + headers["Accept"] = "application/scim+json" + + return self._version.delete(method='DELETE', uri=self._uri, headers=headers) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + + headers = values.of({}) + + + + headers["Accept"] = "application/scim+json" + + return await self._version.delete_async(method='DELETE', uri=self._uri, headers=headers) + + + def fetch(self) -> UserInstance: + """ + Fetch1 the UserInstance + + + :returns: The fetched UserInstance + """ + + + headers = values.of({}) + + + headers["Accept"] = "application/scim+json" + + payload = self._version.fetch(method='GET', uri=self._uri , headers=headers) + + return UserInstance( + self._version, + payload, + organization_sid=self._solution['organization_sid'], + id=self._solution['id'], + + ) + + async def fetch_async(self) -> UserInstance: + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + + headers = values.of({}) + + + headers["Accept"] = "application/scim+json" + + payload = await self._version.fetch_async(method='GET', uri=self._uri , headers=headers) + + return UserInstance( + self._version, + payload, + organization_sid=self._solution['organization_sid'], + id=self._solution['id'], + + ) + + + def update(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> UserInstance: + """ + Update the UserInstance + + :param scim_user: + :param if_match: + + :returns: The updated UserInstance + """ + data = scim_user.to_dict() + + headers = values.of({}) + + + if not (if_match is values.unset or (isinstance(if_match, str) and not if_match)): + headers['If-Match'] = if_match + + headers["Content-Type"] = "application/json" + + headers["Content-Type"] = "application/scim+json" + + + headers["Accept"] = "application/scim+json" + + + payload = self._version.update(method='PUT', uri=self._uri, data=data, headers=headers) + + return UserInstance( + self._version, + payload, + organization_sid=self._solution['organization_sid'], + id=self._solution['id'] + ) + + async def update_async(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> UserInstance: + """ + Asynchronous coroutine to update the UserInstance + + :param scim_user: + :param if_match: + + :returns: The updated UserInstance + """ + data = scim_user.to_dict() + + headers = values.of({}) + + + if not (if_match is values.unset or (isinstance(if_match, str) and not if_match)): + headers['If-Match'] = if_match + + + headers["Content-Type"] = "application/json" + + headers["Content-Type"] = "application/scim+json" + + + headers["Accept"] = "application/scim+json" + + + payload = await self._version.update_async(method='PUT', uri=self._uri, data=data, headers=headers) + + return UserInstance( + self._version, + payload, + organization_sid=self._solution['organization_sid'], + id=self._solution['id'] + ) + + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + + + + + + + + + + + +class UserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserInstance: + """ + Build an instance of UserInstance + + :param payload: Payload response from the API + """ + return UserInstance(self._version, payload, organization_sid=self._solution["organization_sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + + + + +class UserList(ListResource): + + class ScimEmailAddress(object): + """ + :ivar primary: Indicates if this email address is the primary one + :ivar value: The actual email address value + :ivar type: The type of email address (e.g., work, home, etc.) + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.primary: Optional[bool] = payload.get("primary") + self.value: Optional[str] = payload.get("value") + self.type: Optional[str] = payload.get("type") + + def to_dict(self): + return { + + "primary": self.primary, + "value": self.value, + "type": self.type, + } + + class ScimMeta(object): + """ + :ivar resource_type: Indicates the type of the resource + :ivar created: The date and time when the resource was created in the system + :ivar last_modified: The date and time when the resource was last modified + :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.resource_type: Optional[str] = payload.get("resource_type") + self.created: Optional[datetime] = payload.get("created") + self.last_modified: Optional[datetime] = payload.get("last_modified") + self.version: Optional[str] = payload.get("version") + + def to_dict(self): + return { + + "resource_type": self.resource_type, + "created": self.created, + "last_modified": self.last_modified, + "version": self.version, + } + + class ScimName(object): + """ + :ivar given_name: The user's first or given name + :ivar family_name: The user's last or family name + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.given_name: Optional[str] = payload.get("given_name") + self.family_name: Optional[str] = payload.get("family_name") + + def to_dict(self): + return { + + "given_name": self.given_name, + "family_name": self.family_name, + } + + class ScimUser(object): + """ + :ivar id: Unique Twilio user sid + :ivar external_id: External unique resource id defined by provisioning client + :ivar user_name: Unique username, MUST be same as primary email address + :ivar display_name: User friendly display name + :ivar name: + :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + :ivar active: Indicates whether the user is active + :ivar locale: User's locale + :ivar timezone: User's time zone + :ivar schemas: An array of URIs that indicate the schemas supported for this user resource + :ivar meta: + :ivar detail: A human-readable description of the error + :ivar scim_type: A scimType error code as defined in RFC7644 + :ivar status: Http status code + :ivar code: Twilio-specific error code + :ivar more_info: Link to Error Code References + """ + + def __init__(self, payload: Dict[str, Any]): + + + self.id: Optional[str] = payload.get("id") + self.external_id: Optional[str] = payload.get("external_id") + self.user_name: Optional[str] = payload.get("user_name") + self.display_name: Optional[str] = payload.get("display_name") + self.name: Optional[UserList.ScimName] = payload.get("name") + self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get("emails") + self.active: Optional[bool] = payload.get("active") + self.locale: Optional[str] = payload.get("locale") + self.timezone: Optional[str] = payload.get("timezone") + self.schemas: Optional[List[str]] = payload.get("schemas") + self.meta: Optional[UserList.ScimMeta] = payload.get("meta") + self.detail: Optional[str] = payload.get("detail") + self.scim_type: Optional[str] = payload.get("scim_type") + self.status: Optional[str] = payload.get("status") + self.code: Optional[int] = payload.get("code") + self.more_info: Optional[str] = payload.get("more_info") + + def to_dict(self): + return { + + "id": self.id, + "externalId": self.external_id, + "userName": self.user_name, + "displayName": self.display_name, + "name": self.name.to_dict() if self.name is not None else None , + "emails": [emails.to_dict() for emails in self.emails] if self.emails is not None else None, + "active": self.active, + "locale": self.locale, + "timezone": self.timezone, + "schemas": self.schemas, + "meta": self.meta.to_dict() if self.meta is not None else None , + "detail": self.detail, + "scimType": self.scim_type, + "status": self.status, + "code": self.code, + "moreInfo": self.more_info, + } + + + def __init__(self, version: Version, organization_sid: str): + """ + Initialize the UserList + + :param version: Version that contains the resource + :param organization_sid: + + """ + super().__init__(version) + + + # Path Solution + self._solution = { 'organization_sid': organization_sid, } + self._uri = '/{organization_sid}/scim/Users'.format(**self._solution) + + + + + + + def create(self, scim_user: ScimUser) -> UserInstance: + """ + Create the UserInstance + + :param scim_user: + + :returns: The created UserInstance + """ + data = scim_user.to_dict() + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + headers["Content-Type"] = "application/json" + + headers["Content-Type"] = "application/scim+json" + + + headers["Accept"] = "application/scim+json" + + + payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) + + return UserInstance(self._version, payload, organization_sid=self._solution['organization_sid']) + + async def create_async(self, scim_user: ScimUser) -> UserInstance: + """ + Asynchronously create the UserInstance + + :param scim_user: + + :returns: The created UserInstance + """ + data = scim_user.to_dict() + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + headers["Content-Type"] = "application/json" + + headers["Content-Type"] = "application/scim+json" + + + headers["Accept"] = "application/scim+json" + + + payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) + + return UserInstance(self._version, payload, organization_sid=self._solution['organization_sid']) + + + def stream(self, + filter: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserInstance]: + """ + Streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str filter: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + filter=filter, + page_size=limits['page_size'] + ) + + return self._version.stream(page, limits['limit']) + + async def stream_async(self, + filter: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserInstance]: + """ + Asynchronously streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str filter: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + filter=filter, + page_size=limits['page_size'] + ) + + return self._version.stream_async(page, limits['limit']) + + def list(self, + filter: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str filter: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list(self.stream( + filter=filter, + limit=limit, + page_size=page_size, + )) + + async def list_async(self, + filter: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Asynchronously lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str filter: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [record async for record in await self.stream_async( + filter=filter, + limit=limit, + page_size=page_size, + )] + + def page(self, + filter: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param filter: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of({ + 'filter': filter, + 'PageToken': page_token, + 'Page': page_number, + 'PageSize': page_size, + }) + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + headers["Accept"] = "application/scim+json" + + + response = self._version.page(method='GET', uri=self._uri, params=data, headers=headers) + return UserPage(self._version, response, self._solution) + + async def page_async(self, + filter: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Asynchronously retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param filter: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of({ + 'filter': filter, + 'PageToken': page_token, + 'Page': page_number, + 'PageSize': page_size, + }) + + headers = values.of({ + 'Content-Type': 'application/x-www-form-urlencoded' + }) + + + headers["Accept"] = "application/scim+json" + + + response = await self._version.page_async(method='GET', uri=self._uri, params=data, headers=headers) + return UserPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserPage: + """ + Retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = self._version.domain.twilio.request( + 'GET', + target_url + ) + return UserPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserPage: + """ + Asynchronously retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = await self._version.domain.twilio.request_async( + 'GET', + target_url + ) + return UserPage(self._version, response, self._solution) + + + + def get(self, id: str) -> UserContext: + """ + Constructs a UserContext + + :param id: + """ + return UserContext(self._version, organization_sid=self._solution['organization_sid'], id=id) + + def __call__(self, id: str) -> UserContext: + """ + Constructs a UserContext + + :param id: + """ + return UserContext(self._version, organization_sid=self._solution['organization_sid'], id=id) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return '' + From 1c8420c193663e06f070b3cdb82ee9015f725bbc Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Wed, 11 Dec 2024 22:43:39 +0530 Subject: [PATCH 24/33] Organization api uptake changes --- twilio/auth_strategy/token_auth_strategy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twilio/auth_strategy/token_auth_strategy.py b/twilio/auth_strategy/token_auth_strategy.py index 0c60f9a06..1989f4b57 100644 --- a/twilio/auth_strategy/token_auth_strategy.py +++ b/twilio/auth_strategy/token_auth_strategy.py @@ -26,7 +26,7 @@ def requires_authentication(self) -> bool: def fetch_token(self): 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() From 644f94ba77f11c42de312f1d197c4eb606a3b7bc Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Wed, 11 Dec 2024 22:45:03 +0530 Subject: [PATCH 25/33] Organization api uptake changes --- twilio/base/client_base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index bcd42117b..b842f3e2c 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -4,6 +4,7 @@ from urllib.parse import urlparse, urlunparse from twilio import __version__ +from twilio.base.exceptions import TwilioException from twilio.http import HttpClient from twilio.http.http_client import TwilioHttpClient from twilio.http.response import Response From 66f3e2871cc2f6abef453e8dd16e354b0995bc61 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Wed, 11 Dec 2024 22:47:50 +0530 Subject: [PATCH 26/33] Organization api uptake changes --- twilio/rest/preview/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/twilio/rest/preview/__init__.py b/twilio/rest/preview/__init__.py index 2f6334ae0..7b099b968 100644 --- a/twilio/rest/preview/__init__.py +++ b/twilio/rest/preview/__init__.py @@ -1,6 +1,7 @@ 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, ) @@ -14,6 +15,14 @@ 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 From 88f6623004956a660ac4a6b572058cd24ced1435 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 12 Dec 2024 11:04:21 +0530 Subject: [PATCH 27/33] removing accept headers for delete operation --- twilio/base/client_base.py | 5 +++++ twilio/rest/preview_iam/versionless/organization/user.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index b842f3e2c..a6e47c4f1 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -101,6 +101,9 @@ def request( else: auth = None + if method == 'DELETE': + del headers["Accept"] + uri = self.get_hostname(uri) filtered_data = self.copy_non_none_values(data) return self.http_client.request( @@ -147,6 +150,8 @@ async def request_async( ) headers = self.get_headers(method, headers) + if method == 'DELETE': + del headers["Accept"] ##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 diff --git a/twilio/rest/preview_iam/versionless/organization/user.py b/twilio/rest/preview_iam/versionless/organization/user.py index 54cd2edd9..3421780de 100644 --- a/twilio/rest/preview_iam/versionless/organization/user.py +++ b/twilio/rest/preview_iam/versionless/organization/user.py @@ -253,7 +253,7 @@ async def fetch_async(self) -> "UserInstance": def update(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> "UserInstance": - """hello3 + """ Update the UserInstance :param scim_user: From 630c28c5c3e6bb8f8f86d76827dbd879fa057441 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 12 Dec 2024 11:28:17 +0530 Subject: [PATCH 28/33] Removing unwanted code --- twilio/base/client_base.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index a6e47c4f1..2adfb59a6 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -262,20 +262,6 @@ 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 From ddb336b4d6038dccaa58a0dae250a90ca06dad38 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 12 Dec 2024 13:10:38 +0530 Subject: [PATCH 29/33] Modified generated files --- twilio/rest/preview_iam/versionless/organization/account.py | 2 +- .../preview_iam/versionless/organization/role_assignment.py | 4 ---- twilio/rest/preview_iam/versionless/organization/user.py | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/twilio/rest/preview_iam/versionless/organization/account.py b/twilio/rest/preview_iam/versionless/organization/account.py index ec5c524c3..e7203f1e8 100644 --- a/twilio/rest/preview_iam/versionless/organization/account.py +++ b/twilio/rest/preview_iam/versionless/organization/account.py @@ -116,7 +116,7 @@ def __init__(self, version: Version, organization_sid: str, account_sid: str): def fetch(self) -> AccountInstance: """ - Fetch1 the AccountInstance + Fetch the AccountInstance :returns: The fetched AccountInstance diff --git a/twilio/rest/preview_iam/versionless/organization/role_assignment.py b/twilio/rest/preview_iam/versionless/organization/role_assignment.py index ec6e932e2..50c2dc219 100644 --- a/twilio/rest/preview_iam/versionless/organization/role_assignment.py +++ b/twilio/rest/preview_iam/versionless/organization/role_assignment.py @@ -298,8 +298,6 @@ def create(self, public_api_create_role_assignment_request: PublicApiCreateRoleA headers["Accept"] = "application/json" - headers["Accept"] = "application/scim+json" - payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) @@ -324,8 +322,6 @@ async def create_async(self, public_api_create_role_assignment_request: PublicAp headers["Accept"] = "application/json" - headers["Accept"] = "application/scim+json" - payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) diff --git a/twilio/rest/preview_iam/versionless/organization/user.py b/twilio/rest/preview_iam/versionless/organization/user.py index 3421780de..038d110b4 100644 --- a/twilio/rest/preview_iam/versionless/organization/user.py +++ b/twilio/rest/preview_iam/versionless/organization/user.py @@ -469,7 +469,7 @@ async def delete_async(self) -> bool: def fetch(self) -> UserInstance: """ - Fetch1 the UserInstance + Fetch the UserInstance :returns: The fetched UserInstance From d79366ecf07ab8a83237ac7dda03ab87b569c8da Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 12 Dec 2024 13:17:46 +0530 Subject: [PATCH 30/33] removing unwamted logs --- twilio/base/version.py | 2 -- twilio/http/http_client.py | 4 ---- 2 files changed, 6 deletions(-) diff --git a/twilio/base/version.py b/twilio/base/version.py index 741752d92..574eee7b5 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -164,8 +164,6 @@ async def fetch_async( timeout=timeout, allow_redirects=allow_redirects, ) - print('response') - print(response) diff --git a/twilio/http/http_client.py b/twilio/http/http_client.py index 348a42c95..e15be8635 100644 --- a/twilio/http/http_client.py +++ b/twilio/http/http_client.py @@ -93,7 +93,6 @@ def request( else: kwargs["data"] = data self.log_request(kwargs) - print(f"\nargs : {kwargs}") self._test_only_last_response = None session = self.session or Session() request = Request(**kwargs) @@ -111,9 +110,6 @@ def request( **settings, ) - print(f"\nresponse : {response.text}") - print(f"\nresponse code : {response}") - self.log_response(response.status_code, response) self._test_only_last_response = Response( From 824ed9f42c854041027b21091aa51ad270d0f262 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 12 Dec 2024 13:48:25 +0530 Subject: [PATCH 31/33] Formatting changes --- twilio/auth_strategy/no_auth_strategy.py | 1 + twilio/base/client_base.py | 5 ----- twilio/base/version.py | 3 --- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/twilio/auth_strategy/no_auth_strategy.py b/twilio/auth_strategy/no_auth_strategy.py index 8b9324aa2..a5bfd6d27 100644 --- a/twilio/auth_strategy/no_auth_strategy.py +++ b/twilio/auth_strategy/no_auth_strategy.py @@ -1,4 +1,5 @@ from auth_type import AuthType +from twilio.auth_strategy.auth_strategy import AuthStrategy class NoAuthStrategy(AuthStrategy): diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index 2adfb59a6..b0205804f 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -90,8 +90,6 @@ def request( """ 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() @@ -153,9 +151,6 @@ async def request_async( if method == 'DELETE': del headers["Accept"] - ##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() diff --git a/twilio/base/version.py b/twilio/base/version.py index 574eee7b5..ed7e86f49 100644 --- a/twilio/base/version.py +++ b/twilio/base/version.py @@ -164,9 +164,6 @@ async def fetch_async( timeout=timeout, allow_redirects=allow_redirects, ) - - - return self._parse_fetch(method, uri, response) def _parse_update(self, method: str, uri: str, response: Response) -> Any: From 3670f03c77581b34a5ed7132927e187817f1d75f Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 12 Dec 2024 13:50:49 +0530 Subject: [PATCH 32/33] make prettier run --- twilio/auth_strategy/token_auth_strategy.py | 6 +- twilio/base/client_base.py | 15 +- twilio/rest/preview/__init__.py | 1 - twilio/rest/preview_iam/PreviewIamBase.py | 2 +- twilio/rest/preview_iam/__init__.py | 9 +- twilio/rest/preview_iam/v1/__init__.py | 2 +- twilio/rest/preview_iam/v1/authorize.py | 109 ++- twilio/rest/preview_iam/v1/token.py | 134 +-- .../rest/preview_iam/versionless/__init__.py | 2 +- .../versionless/organization/__init__.py | 75 +- .../versionless/organization/account.py | 286 +++--- .../organization/role_assignment.py | 430 +++++---- .../versionless/organization/user.py | 860 +++++++++--------- 13 files changed, 938 insertions(+), 993 deletions(-) diff --git a/twilio/auth_strategy/token_auth_strategy.py b/twilio/auth_strategy/token_auth_strategy.py index 1989f4b57..ee51f05a4 100644 --- a/twilio/auth_strategy/token_auth_strategy.py +++ b/twilio/auth_strategy/token_auth_strategy.py @@ -27,7 +27,11 @@ def requires_authentication(self) -> bool: def fetch_token(self): if self.token is None or self.token == "" or self.is_token_expired(self.token): with self.lock: - if self.token is None or self.token == "" or self.is_token_expired(self.token): + 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() diff --git a/twilio/base/client_base.py b/twilio/base/client_base.py index b0205804f..b16f85bf5 100644 --- a/twilio/base/client_base.py +++ b/twilio/base/client_base.py @@ -4,7 +4,6 @@ from urllib.parse import urlparse, urlunparse from twilio import __version__ -from twilio.base.exceptions import TwilioException from twilio.http import HttpClient from twilio.http.http_client import TwilioHttpClient from twilio.http.response import Response @@ -99,7 +98,7 @@ def request( else: auth = None - if method == 'DELETE': + if method == "DELETE": del headers["Accept"] uri = self.get_hostname(uri) @@ -148,7 +147,7 @@ async def request_async( ) headers = self.get_headers(method, headers) - if method == 'DELETE': + if method == "DELETE": del headers["Accept"] if self.credential_provider: @@ -174,9 +173,15 @@ async def request_async( 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} + 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 [ + 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]: diff --git a/twilio/rest/preview/__init__.py b/twilio/rest/preview/__init__.py index 2f6334ae0..501ae417d 100644 --- a/twilio/rest/preview/__init__.py +++ b/twilio/rest/preview/__init__.py @@ -15,7 +15,6 @@ class Preview(PreviewBase): - @property def authorization_documents(self) -> AuthorizationDocumentList: warn( diff --git a/twilio/rest/preview_iam/PreviewIamBase.py b/twilio/rest/preview_iam/PreviewIamBase.py index e512bb2c2..d8735295d 100644 --- a/twilio/rest/preview_iam/PreviewIamBase.py +++ b/twilio/rest/preview_iam/PreviewIamBase.py @@ -16,6 +16,7 @@ from twilio.rest.preview_iam.v1 import V1 from twilio.rest.preview_iam.versionless import Versionless + class PreviewIamBase(Domain): def __init__(self, twilio: Client): """ @@ -27,7 +28,6 @@ def __init__(self, twilio: Client): self._versionless: Optional[Versionless] = None self._v1: Optional[V1] = None - @property def versionless(self) -> Versionless: """ diff --git a/twilio/rest/preview_iam/__init__.py b/twilio/rest/preview_iam/__init__.py index 9d10225fb..910343622 100644 --- a/twilio/rest/preview_iam/__init__.py +++ b/twilio/rest/preview_iam/__init__.py @@ -1,12 +1,5 @@ -from warnings import warn from twilio.rest.preview_iam.PreviewIamBase import PreviewIamBase -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, ) @@ -17,12 +10,12 @@ OrganizationList, ) + class PreviewIam(PreviewIamBase): @property def organization(self) -> OrganizationList: return self.versionless.organization - @property def authorize(self) -> AuthorizeList: return self.v1.authorize diff --git a/twilio/rest/preview_iam/v1/__init__.py b/twilio/rest/preview_iam/v1/__init__.py index 7bb47b159..224327779 100644 --- a/twilio/rest/preview_iam/v1/__init__.py +++ b/twilio/rest/preview_iam/v1/__init__.py @@ -30,7 +30,7 @@ def __init__(self, domain: Domain): super().__init__(domain, "v1") self._authorize: Optional[AuthorizeList] = None self._token: Optional[TokenList] = None - + @property def authorize(self) -> AuthorizeList: if self._authorize is None: diff --git a/twilio/rest/preview_iam/v1/authorize.py b/twilio/rest/preview_iam/v1/authorize.py index f63090521..051f13a8e 100644 --- a/twilio/rest/preview_iam/v1/authorize.py +++ b/twilio/rest/preview_iam/v1/authorize.py @@ -12,20 +12,15 @@ Do not edit the class manually. """ - -from datetime import date, datetime -from decimal import Decimal -from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import deserialize, serialize, values +from typing import Any, Dict, Optional, Union +from twilio.base import values from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource from twilio.base.version import Version - class AuthorizeInstance(InstanceResource): - """ :ivar redirect_to: The callback URL """ @@ -33,95 +28,99 @@ class AuthorizeInstance(InstanceResource): def __init__(self, version: Version, payload: Dict[str, Any]): super().__init__(version) - self.redirect_to: Optional[str] = payload.get("redirect_to") - - - def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - - return '' - + return "" class AuthorizeList(ListResource): - + def __init__(self, version: Version): """ Initialize the AuthorizeList :param version: Version that contains the resource - + """ super().__init__(version) - - self._uri = '/authorize' - - - - def fetch(self, response_type: Union[str, object]=values.unset, client_id: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, scope: Union[str, object]=values.unset, state: Union[str, object]=values.unset) -> AuthorizeInstance: + self._uri = "/authorize" + + def fetch( + self, + response_type: Union[str, object] = values.unset, + client_id: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + ) -> AuthorizeInstance: """ Asynchronously fetch the AuthorizeInstance :param response_type: Response Type:param client_id: The Client Identifier:param redirect_uri: The url to which response will be redirected to:param scope: The scope of the access request:param state: An opaque value which can be used to maintain state between the request and callback :returns: The fetched AuthorizeInstance """ - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - params = values.of({ - 'response_type': response_type, - 'client_id': client_id, - 'redirect_uri': redirect_uri, - 'scope': scope, - 'state': state, - - }) - - payload = self._version.fetch(method='GET', uri=self._uri, headers=headers, params=params) + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + params = values.of( + { + "response_type": response_type, + "client_id": client_id, + "redirect_uri": redirect_uri, + "scope": scope, + "state": state, + } + ) + + payload = self._version.fetch( + method="GET", uri=self._uri, headers=headers, params=params + ) return AuthorizeInstance(self._version, payload) - async def fetch_async(self, response_type: Union[str, object]=values.unset, client_id: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, scope: Union[str, object]=values.unset, state: Union[str, object]=values.unset) -> AuthorizeInstance: + async def fetch_async( + self, + response_type: Union[str, object] = values.unset, + client_id: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + ) -> AuthorizeInstance: """ Asynchronously fetch the AuthorizeInstance :param response_type: Response Type:param client_id: The Client Identifier:param redirect_uri: The url to which response will be redirected to:param scope: The scope of the access request:param state: An opaque value which can be used to maintain state between the request and callback :returns: The fetched AuthorizeInstance """ - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - params = values.of({ - 'response_type': response_type, - 'client_id': client_id, - 'redirect_uri': redirect_uri, - 'scope': scope, - 'state': state, - - }) - - payload = await self._version.fetch_async(method='GET', uri=self._uri, headers=headers, params=params) + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + params = values.of( + { + "response_type": response_type, + "client_id": client_id, + "redirect_uri": redirect_uri, + "scope": scope, + "state": state, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, headers=headers, params=params + ) return AuthorizeInstance(self._version, payload) - - def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - return '' - + return "" diff --git a/twilio/rest/preview_iam/v1/token.py b/twilio/rest/preview_iam/v1/token.py index d6d7fc463..81eed237f 100644 --- a/twilio/rest/preview_iam/v1/token.py +++ b/twilio/rest/preview_iam/v1/token.py @@ -12,70 +12,66 @@ Do not edit the class manually. """ - -from datetime import date, datetime -from decimal import Decimal -from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import deserialize, serialize, values +from typing import Any, Dict, Optional, Union +from twilio.base import values from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource from twilio.base.version import Version - class TokenInstance(InstanceResource): - """ :ivar access_token: Token which carries the necessary information to access a Twilio resource directly. :ivar refresh_token: Token which carries the information necessary to get a new access token. :ivar id_token: Token which carries the information necessary of user profile. :ivar token_type: Token type - :ivar expires_in: + :ivar expires_in: """ def __init__(self, version: Version, payload: Dict[str, Any]): super().__init__(version) - self.access_token: Optional[str] = payload.get("access_token") self.refresh_token: Optional[str] = payload.get("refresh_token") self.id_token: Optional[str] = payload.get("id_token") self.token_type: Optional[str] = payload.get("token_type") self.expires_in: Optional[int] = payload.get("expires_in") - - - def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - - return '' - + return "" class TokenList(ListResource): - + def __init__(self, version: Version): """ Initialize the TokenList :param version: Version that contains the resource - + """ super().__init__(version) - - self._uri = '/token' - - - - def create(self, grant_type: str, client_id: str, client_secret: Union[str, object]=values.unset, code: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, audience: Union[str, object]=values.unset, refresh_token: Union[str, object]=values.unset, scope: Union[str, object]=values.unset) -> TokenInstance: + self._uri = "/token" + + def create( + self, + grant_type: str, + client_id: str, + client_secret: Union[str, object] = values.unset, + code: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + audience: Union[str, object] = values.unset, + refresh_token: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + ) -> TokenInstance: """ Create the TokenInstance @@ -87,30 +83,41 @@ def create(self, grant_type: str, client_id: str, client_secret: Union[str, obje :param audience: The targeted audience uri :param refresh_token: JWT token related to refresh access token. :param scope: The scope of token - + :returns: The created TokenInstance """ - - data = values.of({ - 'grant_type': grant_type, - 'client_id': client_id, - 'client_secret': client_secret, - 'code': code, - 'redirect_uri': redirect_uri, - 'audience': audience, - 'refresh_token': refresh_token, - 'scope': scope, - }) - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - - payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) + + data = values.of( + { + "grant_type": grant_type, + "client_id": client_id, + "client_secret": client_secret, + "code": code, + "redirect_uri": redirect_uri, + "audience": audience, + "refresh_token": refresh_token, + "scope": scope, + } + ) + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) return TokenInstance(self._version, payload) - async def create_async(self, grant_type: str, client_id: str, client_secret: Union[str, object]=values.unset, code: Union[str, object]=values.unset, redirect_uri: Union[str, object]=values.unset, audience: Union[str, object]=values.unset, refresh_token: Union[str, object]=values.unset, scope: Union[str, object]=values.unset) -> TokenInstance: + async def create_async( + self, + grant_type: str, + client_id: str, + client_secret: Union[str, object] = values.unset, + code: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + audience: Union[str, object] = values.unset, + refresh_token: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + ) -> TokenInstance: """ Asynchronously create the TokenInstance @@ -122,31 +129,29 @@ async def create_async(self, grant_type: str, client_id: str, client_secret: Uni :param audience: The targeted audience uri :param refresh_token: JWT token related to refresh access token. :param scope: The scope of token - + :returns: The created TokenInstance """ - - data = values.of({ - 'grant_type': grant_type, - 'client_id': client_id, - 'client_secret': client_secret, - 'code': code, - 'redirect_uri': redirect_uri, - 'audience': audience, - 'refresh_token': refresh_token, - 'scope': scope, - }) - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - - payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) - - return TokenInstance(self._version, payload) - + data = values.of( + { + "grant_type": grant_type, + "client_id": client_id, + "client_secret": client_secret, + "code": code, + "redirect_uri": redirect_uri, + "audience": audience, + "refresh_token": refresh_token, + "scope": scope, + } + ) + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + return TokenInstance(self._version, payload) def __repr__(self) -> str: """ @@ -154,5 +159,4 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return '' - + return "" diff --git a/twilio/rest/preview_iam/versionless/__init__.py b/twilio/rest/preview_iam/versionless/__init__.py index 946f389d9..7d6d210f1 100644 --- a/twilio/rest/preview_iam/versionless/__init__.py +++ b/twilio/rest/preview_iam/versionless/__init__.py @@ -28,7 +28,7 @@ def __init__(self, domain: Domain): """ super().__init__(domain, "Organizations") self._organization: Optional[OrganizationList] = None - + @property def organization(self) -> OrganizationList: if self._organization is None: diff --git a/twilio/rest/preview_iam/versionless/organization/__init__.py b/twilio/rest/preview_iam/versionless/organization/__init__.py index 00b99a9a2..ce6e70e97 100644 --- a/twilio/rest/preview_iam/versionless/organization/__init__.py +++ b/twilio/rest/preview_iam/versionless/organization/__init__.py @@ -12,22 +12,19 @@ Do not edit the class manually. """ - -from datetime import date, datetime -from decimal import Decimal -from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import deserialize, serialize, values +from typing import Optional from twilio.base.instance_context import InstanceContext from twilio.base.list_resource import ListResource from twilio.base.version import Version 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.versionless.organization.role_assignment import ( + RoleAssignmentList, +) from twilio.rest.preview_iam.versionless.organization.user import UserList - class OrganizationContext(InstanceContext): def __init__(self, version: Version, organization_sid: str): @@ -35,23 +32,20 @@ def __init__(self, version: Version, organization_sid: str): Initialize the OrganizationContext :param version: Version that contains the resource - :param organization_sid: + :param organization_sid: """ super().__init__(version) - # Path Solution - self._solution = { - 'organization_sid': organization_sid, + self._solution = { + "organization_sid": organization_sid, } - self._uri = '/{organization_sid}'.format(**self._solution) - + self._uri = "/{organization_sid}".format(**self._solution) + self._accounts: Optional[AccountList] = None self._role_assignments: Optional[RoleAssignmentList] = None self._users: Optional[UserList] = None - - - + @property def accounts(self) -> AccountList: """ @@ -59,11 +53,11 @@ def accounts(self) -> AccountList: """ if self._accounts is None: self._accounts = AccountList( - self._version, - self._solution['organization_sid'], + self._version, + self._solution["organization_sid"], ) return self._accounts - + @property def role_assignments(self) -> RoleAssignmentList: """ @@ -71,11 +65,11 @@ def role_assignments(self) -> RoleAssignmentList: """ if self._role_assignments is None: self._role_assignments = RoleAssignmentList( - self._version, - self._solution['organization_sid'], + self._version, + self._solution["organization_sid"], ) return self._role_assignments - + @property def users(self) -> UserList: """ @@ -83,57 +77,45 @@ def users(self) -> UserList: """ if self._users is None: self._users = UserList( - self._version, - self._solution['organization_sid'], + self._version, + self._solution["organization_sid"], ) return self._users - + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) - return ''.format(context) - + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) class OrganizationList(ListResource): - + def __init__(self, version: Version): """ Initialize the OrganizationList :param version: Version that contains the resource - + """ super().__init__(version) - - - - - - - - - - - def get(self, organization_sid: str) -> OrganizationContext: """ Constructs a OrganizationContext - - :param organization_sid: + + :param organization_sid: """ return OrganizationContext(self._version, organization_sid=organization_sid) def __call__(self, organization_sid: str) -> OrganizationContext: """ Constructs a OrganizationContext - - :param organization_sid: + + :param organization_sid: """ return OrganizationContext(self._version, organization_sid=organization_sid) @@ -143,5 +125,4 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return '' - + return "" diff --git a/twilio/rest/preview_iam/versionless/organization/account.py b/twilio/rest/preview_iam/versionless/organization/account.py index e7203f1e8..f1c801986 100644 --- a/twilio/rest/preview_iam/versionless/organization/account.py +++ b/twilio/rest/preview_iam/versionless/organization/account.py @@ -12,11 +12,9 @@ Do not edit the class manually. """ - -from datetime import date, datetime -from decimal import Decimal +from datetime import datetime from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import deserialize, serialize, values +from twilio.base import deserialize, values from twilio.base.instance_context import InstanceContext from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource @@ -25,8 +23,6 @@ class AccountInstance(InstanceResource): - - """ :ivar account_sid: Twilio account sid :ivar friendly_name: Account friendly name @@ -35,18 +31,24 @@ class AccountInstance(InstanceResource): :ivar date_created: The date and time when the account was created in the system """ - def __init__(self, version: Version, payload: Dict[str, Any], organization_sid: str, account_sid: Optional[str] = None): + def __init__( + self, + version: Version, + payload: Dict[str, Any], + organization_sid: str, + account_sid: Optional[str] = None, + ): super().__init__(version) - self.account_sid: Optional[str] = payload.get("account_sid") self.friendly_name: Optional[str] = payload.get("friendly_name") self.status: Optional[str] = payload.get("status") self.owner_sid: Optional[str] = payload.get("owner_sid") - self.date_created: Optional[datetime] = deserialize.iso8601_datetime(payload.get("date_created")) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) - - self._solution = { + self._solution = { "organization_sid": organization_sid, "account_sid": account_sid or self.account_sid, } @@ -61,14 +63,17 @@ def _proxy(self) -> "AccountContext": :returns: AccountContext for this AccountInstance """ if self._context is None: - self._context = AccountContext(self._version, organization_sid=self._solution['organization_sid'], account_sid=self._solution['account_sid'],) + self._context = AccountContext( + self._version, + organization_sid=self._solution["organization_sid"], + account_sid=self._solution["account_sid"], + ) return self._context - - + def fetch(self) -> "AccountInstance": """ Fetch the AccountInstance - + :returns: The fetched AccountInstance """ @@ -77,20 +82,21 @@ def fetch(self) -> "AccountInstance": async def fetch_async(self) -> "AccountInstance": """ Asynchronous coroutine to fetch the AccountInstance - + :returns: The fetched AccountInstance """ return await self._proxy.fetch_async() - + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) - return ''.format(context) + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + class AccountContext(InstanceContext): @@ -99,81 +105,72 @@ def __init__(self, version: Version, organization_sid: str, account_sid: str): Initialize the AccountContext :param version: Version that contains the resource - :param organization_sid: - :param account_sid: + :param organization_sid: + :param account_sid: """ super().__init__(version) - # Path Solution - self._solution = { - 'organization_sid': organization_sid, - 'account_sid': account_sid, + self._solution = { + "organization_sid": organization_sid, + "account_sid": account_sid, } - self._uri = '/{organization_sid}/Accounts/{account_sid}'.format(**self._solution) - - - + self._uri = "/{organization_sid}/Accounts/{account_sid}".format( + **self._solution + ) + def fetch(self) -> AccountInstance: """ Fetch the AccountInstance - + :returns: The fetched AccountInstance """ - headers = values.of({}) - - + headers["Accept"] = "application/json" - - payload = self._version.fetch(method='GET', uri=self._uri , headers=headers) + + payload = self._version.fetch(method="GET", uri=self._uri, headers=headers) return AccountInstance( self._version, payload, - organization_sid=self._solution['organization_sid'], - account_sid=self._solution['account_sid'], - + organization_sid=self._solution["organization_sid"], + account_sid=self._solution["account_sid"], ) async def fetch_async(self) -> AccountInstance: """ Asynchronous coroutine to fetch the AccountInstance - + :returns: The fetched AccountInstance """ - headers = values.of({}) - - + headers["Accept"] = "application/json" - - payload = await self._version.fetch_async(method='GET', uri=self._uri , headers=headers) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, headers=headers + ) return AccountInstance( self._version, payload, - organization_sid=self._solution['organization_sid'], - account_sid=self._solution['account_sid'], - + organization_sid=self._solution["organization_sid"], + account_sid=self._solution["account_sid"], ) - - + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) - return ''.format(context) - - - + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) class AccountPage(Page): @@ -184,7 +181,9 @@ def get_instance(self, payload: Dict[str, Any]) -> AccountInstance: :param payload: Payload response from the API """ - return AccountInstance(self._version, payload, organization_sid=self._solution["organization_sid"]) + return AccountInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) def __repr__(self) -> str: """ @@ -195,31 +194,26 @@ def __repr__(self) -> str: return "" - - - class AccountList(ListResource): - + def __init__(self, version: Version, organization_sid: str): """ Initialize the AccountList :param version: Version that contains the resource - :param organization_sid: - + :param organization_sid: + """ super().__init__(version) - # Path Solution - self._solution = { 'organization_sid': organization_sid, } - self._uri = '/{organization_sid}/Accounts'.format(**self._solution) - - - - - def stream(self, - + self._solution = { + "organization_sid": organization_sid, + } + self._uri = "/{organization_sid}/Accounts".format(**self._solution) + + def stream( + self, limit: Optional[int] = None, page_size: Optional[int] = None, ) -> Iterator[AccountInstance]: @@ -228,7 +222,7 @@ def stream(self, This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - + :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -239,14 +233,12 @@ def stream(self, :returns: Generator that will yield up to limit results """ limits = self._version.read_limits(limit, page_size) - page = self.page( - page_size=limits['page_size'] - ) + page = self.page(page_size=limits["page_size"]) - return self._version.stream(page, limits['limit']) + return self._version.stream(page, limits["limit"]) - async def stream_async(self, - + async def stream_async( + self, limit: Optional[int] = None, page_size: Optional[int] = None, ) -> AsyncIterator[AccountInstance]: @@ -255,7 +247,7 @@ async def stream_async(self, This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - + :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -266,14 +258,12 @@ async def stream_async(self, :returns: Generator that will yield up to limit results """ limits = self._version.read_limits(limit, page_size) - page = await self.page_async( - page_size=limits['page_size'] - ) + page = await self.page_async(page_size=limits["page_size"]) - return self._version.stream_async(page, limits['limit']) + return self._version.stream_async(page, limits["limit"]) - def list(self, - + def list( + self, limit: Optional[int] = None, page_size: Optional[int] = None, ) -> List[AccountInstance]: @@ -281,7 +271,7 @@ def list(self, Lists AccountInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - + :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -291,13 +281,15 @@ def list(self, :returns: list that will contain up to limit results """ - return list(self.stream( - limit=limit, - page_size=page_size, - )) + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) - async def list_async(self, - + async def list_async( + self, limit: Optional[int] = None, page_size: Optional[int] = None, ) -> List[AccountInstance]: @@ -305,7 +297,7 @@ async def list_async(self, Asynchronously lists AccountInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - + :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -315,13 +307,16 @@ async def list_async(self, :returns: list that will contain up to limit results """ - return [record async for record in await self.stream_async( - limit=limit, - page_size=page_size, - )] + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] - def page(self, - + def page( + self, page_token: Union[str, object] = values.unset, page_number: Union[int, object] = values.unset, page_size: Union[int, object] = values.unset, @@ -329,32 +324,32 @@ def page(self, """ Retrieve a single page of AccountInstance records from the API. Request is executed immediately - + :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of AccountInstance """ - data = values.of({ - 'PageToken': page_token, - 'Page': page_number, - 'PageSize': page_size, - }) - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Accept"] = "application/json" - - response = self._version.page(method='GET', uri=self._uri, params=data, headers=headers) + response = self._version.page( + method="GET", uri=self._uri, params=data, headers=headers + ) return AccountPage(self._version, response, self._solution) - async def page_async(self, - + async def page_async( + self, page_token: Union[str, object] = values.unset, page_number: Union[int, object] = values.unset, page_size: Union[int, object] = values.unset, @@ -362,28 +357,28 @@ async def page_async(self, """ Asynchronously retrieve a single page of AccountInstance records from the API. Request is executed immediately - + :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of AccountInstance """ - data = values.of({ - 'PageToken': page_token, - 'Page': page_number, - 'PageSize': page_size, - }) - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Accept"] = "application/json" - - response = await self._version.page_async(method='GET', uri=self._uri, params=data, headers=headers) + response = await self._version.page_async( + method="GET", uri=self._uri, params=data, headers=headers + ) return AccountPage(self._version, response, self._solution) def get_page(self, target_url: str) -> AccountPage: @@ -395,10 +390,7 @@ def get_page(self, target_url: str) -> AccountPage: :returns: Page of AccountInstance """ - response = self._version.domain.twilio.request( - 'GET', - target_url - ) + response = self._version.domain.twilio.request("GET", target_url) return AccountPage(self._version, response, self._solution) async def get_page_async(self, target_url: str) -> AccountPage: @@ -410,29 +402,32 @@ async def get_page_async(self, target_url: str) -> AccountPage: :returns: Page of AccountInstance """ - response = await self._version.domain.twilio.request_async( - 'GET', - target_url - ) + response = await self._version.domain.twilio.request_async("GET", target_url) return AccountPage(self._version, response, self._solution) - - def get(self, account_sid: str) -> AccountContext: """ Constructs a AccountContext - - :param account_sid: + + :param account_sid: """ - return AccountContext(self._version, organization_sid=self._solution['organization_sid'], account_sid=account_sid) + return AccountContext( + self._version, + organization_sid=self._solution["organization_sid"], + account_sid=account_sid, + ) def __call__(self, account_sid: str) -> AccountContext: """ Constructs a AccountContext - - :param account_sid: + + :param account_sid: """ - return AccountContext(self._version, organization_sid=self._solution['organization_sid'], account_sid=account_sid) + return AccountContext( + self._version, + organization_sid=self._solution["organization_sid"], + account_sid=account_sid, + ) def __repr__(self) -> str: """ @@ -440,5 +435,4 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return '' - + return "" diff --git a/twilio/rest/preview_iam/versionless/organization/role_assignment.py b/twilio/rest/preview_iam/versionless/organization/role_assignment.py index 50c2dc219..9d356f2c7 100644 --- a/twilio/rest/preview_iam/versionless/organization/role_assignment.py +++ b/twilio/rest/preview_iam/versionless/organization/role_assignment.py @@ -12,11 +12,8 @@ Do not edit the class manually. """ - -from datetime import date, datetime -from decimal import Decimal from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import deserialize, serialize, values +from twilio.base import values from twilio.base.instance_context import InstanceContext from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource @@ -28,28 +25,24 @@ class RoleAssignmentInstance(InstanceResource): class PublicApiCreateRoleAssignmentRequest(object): """ - :ivar role_sid: Twilio Role Sid representing assigned role - :ivar scope: Twilio Sid representing scope of this assignment - :ivar identity: Twilio Sid representing identity of this assignment + :ivar role_sid: Twilio Role Sid representing assigned role + :ivar scope: Twilio Sid representing scope of this assignment + :ivar identity: Twilio Sid representing identity of this assignment """ def __init__(self, payload: Dict[str, Any]): - self.role_sid: Optional[str] = payload.get("role_sid") self.scope: Optional[str] = payload.get("scope") self.identity: Optional[str] = payload.get("identity") def to_dict(self): return { - - "role_sid": self.role_sid, - "scope": self.scope, - "identity": self.identity, + "role_sid": self.role_sid, + "scope": self.scope, + "identity": self.identity, } - - """ :ivar sid: Twilio Role Assignment Sid representing this role assignment :ivar role_sid: Twilio Role Sid representing assigned role @@ -61,10 +54,15 @@ def to_dict(self): :ivar status: HTTP response status code """ - def __init__(self, version: Version, payload: Dict[str, Any], organization_sid: str, sid: Optional[str] = None): + def __init__( + self, + version: Version, + payload: Dict[str, Any], + organization_sid: str, + sid: Optional[str] = None, + ): super().__init__(version) - self.sid: Optional[str] = payload.get("sid") self.role_sid: Optional[str] = payload.get("role_sid") self.scope: Optional[str] = payload.get("scope") @@ -74,8 +72,7 @@ def __init__(self, version: Version, payload: Dict[str, Any], organization_sid: self.more_info: Optional[str] = payload.get("moreInfo") self.status: Optional[int] = payload.get("status") - - self._solution = { + self._solution = { "organization_sid": organization_sid, "sid": sid or self.sid, } @@ -90,128 +87,122 @@ def _proxy(self) -> "RoleAssignmentContext": :returns: RoleAssignmentContext for this RoleAssignmentInstance """ if self._context is None: - self._context = RoleAssignmentContext(self._version, organization_sid=self._solution['organization_sid'], sid=self._solution['sid'],) + self._context = RoleAssignmentContext( + self._version, + organization_sid=self._solution["organization_sid"], + sid=self._solution["sid"], + ) return self._context - - + def delete(self) -> bool: """ Deletes the RoleAssignmentInstance - + :returns: True if delete succeeds, False otherwise """ return self._proxy.delete() + async def delete_async(self) -> bool: """ Asynchronous coroutine that deletes the RoleAssignmentInstance - + :returns: True if delete succeeds, False otherwise """ return await self._proxy.delete_async() - + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) - return ''.format(context) + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + class RoleAssignmentContext(InstanceContext): class PublicApiCreateRoleAssignmentRequest(object): """ - :ivar role_sid: Twilio Role Sid representing assigned role - :ivar scope: Twilio Sid representing scope of this assignment - :ivar identity: Twilio Sid representing identity of this assignment + :ivar role_sid: Twilio Role Sid representing assigned role + :ivar scope: Twilio Sid representing scope of this assignment + :ivar identity: Twilio Sid representing identity of this assignment """ def __init__(self, payload: Dict[str, Any]): - self.role_sid: Optional[str] = payload.get("role_sid") self.scope: Optional[str] = payload.get("scope") self.identity: Optional[str] = payload.get("identity") def to_dict(self): return { - - "role_sid": self.role_sid, - "scope": self.scope, - "identity": self.identity, + "role_sid": self.role_sid, + "scope": self.scope, + "identity": self.identity, } - def __init__(self, version: Version, organization_sid: str, sid: str): """ Initialize the RoleAssignmentContext :param version: Version that contains the resource - :param organization_sid: - :param sid: + :param organization_sid: + :param sid: """ super().__init__(version) - # Path Solution - self._solution = { - 'organization_sid': organization_sid, - 'sid': sid, + self._solution = { + "organization_sid": organization_sid, + "sid": sid, } - self._uri = '/{organization_sid}/RoleAssignments/{sid}'.format(**self._solution) - - - + self._uri = "/{organization_sid}/RoleAssignments/{sid}".format(**self._solution) + def delete(self) -> bool: """ Deletes the RoleAssignmentInstance - + :returns: True if delete succeeds, False otherwise """ - headers = values.of({}) - - - + headers["Accept"] = "application/scim+json" - - return self._version.delete(method='DELETE', uri=self._uri, headers=headers) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) async def delete_async(self) -> bool: """ Asynchronous coroutine that deletes the RoleAssignmentInstance - + :returns: True if delete succeeds, False otherwise """ - + headers = values.of({}) - - - + headers["Accept"] = "application/scim+json" - - return await self._version.delete_async(method='DELETE', uri=self._uri, headers=headers) - - + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) - return ''.format(context) - - - - - + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) class RoleAssignmentPage(Page): @@ -222,7 +213,9 @@ def get_instance(self, payload: Dict[str, Any]) -> RoleAssignmentInstance: :param payload: Payload response from the API """ - return RoleAssignmentInstance(self._version, payload, organization_sid=self._solution["organization_sid"]) + return RoleAssignmentInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) def __repr__(self) -> str: """ @@ -233,103 +226,100 @@ def __repr__(self) -> str: return "" - - - class RoleAssignmentList(ListResource): - + class PublicApiCreateRoleAssignmentRequest(object): """ - :ivar role_sid: Twilio Role Sid representing assigned role - :ivar scope: Twilio Sid representing scope of this assignment - :ivar identity: Twilio Sid representing identity of this assignment + :ivar role_sid: Twilio Role Sid representing assigned role + :ivar scope: Twilio Sid representing scope of this assignment + :ivar identity: Twilio Sid representing identity of this assignment """ def __init__(self, payload: Dict[str, Any]): - self.role_sid: Optional[str] = payload.get("role_sid") self.scope: Optional[str] = payload.get("scope") self.identity: Optional[str] = payload.get("identity") def to_dict(self): return { - - "role_sid": self.role_sid, - "scope": self.scope, - "identity": self.identity, + "role_sid": self.role_sid, + "scope": self.scope, + "identity": self.identity, } - def __init__(self, version: Version, organization_sid: str): """ Initialize the RoleAssignmentList :param version: Version that contains the resource - :param organization_sid: - + :param organization_sid: + """ super().__init__(version) - # Path Solution - self._solution = { 'organization_sid': organization_sid, } - self._uri = '/{organization_sid}/RoleAssignments'.format(**self._solution) - - - - - def create(self, public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest) -> RoleAssignmentInstance: + self._solution = { + "organization_sid": organization_sid, + } + self._uri = "/{organization_sid}/RoleAssignments".format(**self._solution) + + def create( + self, + public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest, + ) -> RoleAssignmentInstance: """ Create the RoleAssignmentInstance - :param public_api_create_role_assignment_request: - + :param public_api_create_role_assignment_request: + :returns: The created RoleAssignmentInstance """ data = public_api_create_role_assignment_request.to_dict() - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Content-Type"] = "application/json" - - + headers["Accept"] = "application/json" - - - payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) - return RoleAssignmentInstance(self._version, payload, organization_sid=self._solution['organization_sid']) + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return RoleAssignmentInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) - async def create_async(self, public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest) -> RoleAssignmentInstance: + async def create_async( + self, + public_api_create_role_assignment_request: PublicApiCreateRoleAssignmentRequest, + ) -> RoleAssignmentInstance: """ Asynchronously create the RoleAssignmentInstance - :param public_api_create_role_assignment_request: - + :param public_api_create_role_assignment_request: + :returns: The created RoleAssignmentInstance """ data = public_api_create_role_assignment_request.to_dict() - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Content-Type"] = "application/json" - - + headers["Accept"] = "application/json" - - - payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) - - return RoleAssignmentInstance(self._version, payload, organization_sid=self._solution['organization_sid']) - - - def stream(self, - + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return RoleAssignmentInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) + + def stream( + self, identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, limit: Optional[int] = None, @@ -340,9 +330,9 @@ def stream(self, This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - - :param str identity: - :param str scope: + + :param str identity: + :param str scope: :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -353,16 +343,12 @@ def stream(self, :returns: Generator that will yield up to limit results """ limits = self._version.read_limits(limit, page_size) - page = self.page( - identity=identity, - scope=scope, - page_size=limits['page_size'] - ) + page = self.page(identity=identity, scope=scope, page_size=limits["page_size"]) - return self._version.stream(page, limits['limit']) + return self._version.stream(page, limits["limit"]) - async def stream_async(self, - + async def stream_async( + self, identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, limit: Optional[int] = None, @@ -373,9 +359,9 @@ async def stream_async(self, This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - - :param str identity: - :param str scope: + + :param str identity: + :param str scope: :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -387,15 +373,13 @@ async def stream_async(self, """ limits = self._version.read_limits(limit, page_size) page = await self.page_async( - identity=identity, - scope=scope, - page_size=limits['page_size'] + identity=identity, scope=scope, page_size=limits["page_size"] ) - return self._version.stream_async(page, limits['limit']) + return self._version.stream_async(page, limits["limit"]) - def list(self, - + def list( + self, identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, limit: Optional[int] = None, @@ -405,9 +389,9 @@ def list(self, Lists RoleAssignmentInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - - :param str identity: - :param str scope: + + :param str identity: + :param str scope: :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -417,15 +401,17 @@ def list(self, :returns: list that will contain up to limit results """ - return list(self.stream( - identity=identity, - scope=scope, - limit=limit, - page_size=page_size, - )) + return list( + self.stream( + identity=identity, + scope=scope, + limit=limit, + page_size=page_size, + ) + ) - async def list_async(self, - + async def list_async( + self, identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, limit: Optional[int] = None, @@ -435,9 +421,9 @@ async def list_async(self, Asynchronously lists RoleAssignmentInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - - :param str identity: - :param str scope: + + :param str identity: + :param str scope: :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -447,15 +433,18 @@ async def list_async(self, :returns: list that will contain up to limit results """ - return [record async for record in await self.stream_async( - identity=identity, - scope=scope, - limit=limit, - page_size=page_size, - )] - - def page(self, - + return [ + record + async for record in await self.stream_async( + identity=identity, + scope=scope, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, page_token: Union[str, object] = values.unset, @@ -465,36 +454,36 @@ def page(self, """ Retrieve a single page of RoleAssignmentInstance records from the API. Request is executed immediately - - :param identity: - :param scope: + + :param identity: + :param scope: :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of RoleAssignmentInstance """ - data = values.of({ - 'Identity': identity, - 'Scope': scope, - 'PageToken': page_token, - 'Page': page_number, - 'PageSize': page_size, - }) - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - + data = values.of( + { + "Identity": identity, + "Scope": scope, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Accept"] = "application/json" - - response = self._version.page(method='GET', uri=self._uri, params=data, headers=headers) + response = self._version.page( + method="GET", uri=self._uri, params=data, headers=headers + ) return RoleAssignmentPage(self._version, response, self._solution) - async def page_async(self, - + async def page_async( + self, identity: Union[str, object] = values.unset, scope: Union[str, object] = values.unset, page_token: Union[str, object] = values.unset, @@ -504,32 +493,32 @@ async def page_async(self, """ Asynchronously retrieve a single page of RoleAssignmentInstance records from the API. Request is executed immediately - - :param identity: - :param scope: + + :param identity: + :param scope: :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of RoleAssignmentInstance """ - data = values.of({ - 'Identity': identity, - 'Scope': scope, - 'PageToken': page_token, - 'Page': page_number, - 'PageSize': page_size, - }) - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - + data = values.of( + { + "Identity": identity, + "Scope": scope, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Accept"] = "application/json" - - response = await self._version.page_async(method='GET', uri=self._uri, params=data, headers=headers) + response = await self._version.page_async( + method="GET", uri=self._uri, params=data, headers=headers + ) return RoleAssignmentPage(self._version, response, self._solution) def get_page(self, target_url: str) -> RoleAssignmentPage: @@ -541,10 +530,7 @@ def get_page(self, target_url: str) -> RoleAssignmentPage: :returns: Page of RoleAssignmentInstance """ - response = self._version.domain.twilio.request( - 'GET', - target_url - ) + response = self._version.domain.twilio.request("GET", target_url) return RoleAssignmentPage(self._version, response, self._solution) async def get_page_async(self, target_url: str) -> RoleAssignmentPage: @@ -556,29 +542,28 @@ async def get_page_async(self, target_url: str) -> RoleAssignmentPage: :returns: Page of RoleAssignmentInstance """ - response = await self._version.domain.twilio.request_async( - 'GET', - target_url - ) + response = await self._version.domain.twilio.request_async("GET", target_url) return RoleAssignmentPage(self._version, response, self._solution) - - def get(self, sid: str) -> RoleAssignmentContext: """ Constructs a RoleAssignmentContext - - :param sid: + + :param sid: """ - return RoleAssignmentContext(self._version, organization_sid=self._solution['organization_sid'], sid=sid) + return RoleAssignmentContext( + self._version, organization_sid=self._solution["organization_sid"], sid=sid + ) def __call__(self, sid: str) -> RoleAssignmentContext: """ Constructs a RoleAssignmentContext - - :param sid: + + :param sid: """ - return RoleAssignmentContext(self._version, organization_sid=self._solution['organization_sid'], sid=sid) + return RoleAssignmentContext( + self._version, organization_sid=self._solution["organization_sid"], sid=sid + ) def __repr__(self) -> str: """ @@ -586,5 +571,4 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return '' - + return "" diff --git a/twilio/rest/preview_iam/versionless/organization/user.py b/twilio/rest/preview_iam/versionless/organization/user.py index 038d110b4..166f76b42 100644 --- a/twilio/rest/preview_iam/versionless/organization/user.py +++ b/twilio/rest/preview_iam/versionless/organization/user.py @@ -12,11 +12,9 @@ Do not edit the class manually. """ - -from datetime import date, datetime -from decimal import Decimal +from datetime import datetime from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator -from twilio.base import deserialize, serialize, values +from twilio.base import values from twilio.base.instance_context import InstanceContext from twilio.base.instance_resource import InstanceResource from twilio.base.list_resource import ListResource @@ -28,37 +26,34 @@ class UserInstance(InstanceResource): class ScimEmailAddress(object): """ - :ivar primary: Indicates if this email address is the primary one - :ivar value: The actual email address value - :ivar type: The type of email address (e.g., work, home, etc.) + :ivar primary: Indicates if this email address is the primary one + :ivar value: The actual email address value + :ivar type: The type of email address (e.g., work, home, etc.) """ def __init__(self, payload: Dict[str, Any]): - self.primary: Optional[bool] = payload.get("primary") self.value: Optional[str] = payload.get("value") self.type: Optional[str] = payload.get("type") def to_dict(self): return { - - "primary": self.primary, - "value": self.value, - "type": self.type, + "primary": self.primary, + "value": self.value, + "type": self.type, } class ScimMeta(object): """ - :ivar resource_type: Indicates the type of the resource - :ivar created: The date and time when the resource was created in the system - :ivar last_modified: The date and time when the resource was last modified - :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. + :ivar resource_type: Indicates the type of the resource + :ivar created: The date and time when the resource was created in the system + :ivar last_modified: The date and time when the resource was last modified + :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. """ def __init__(self, payload: Dict[str, Any]): - self.resource_type: Optional[str] = payload.get("resource_type") self.created: Optional[datetime] = payload.get("created") self.last_modified: Optional[datetime] = payload.get("last_modified") @@ -66,61 +61,59 @@ def __init__(self, payload: Dict[str, Any]): def to_dict(self): return { - - "resource_type": self.resource_type, - "created": self.created, - "last_modified": self.last_modified, - "version": self.version, + "resource_type": self.resource_type, + "created": self.created, + "last_modified": self.last_modified, + "version": self.version, } class ScimName(object): """ - :ivar given_name: The user's first or given name - :ivar family_name: The user's last or family name + :ivar given_name: The user's first or given name + :ivar family_name: The user's last or family name """ def __init__(self, payload: Dict[str, Any]): - self.given_name: Optional[str] = payload.get("given_name") self.family_name: Optional[str] = payload.get("family_name") def to_dict(self): return { - - "given_name": self.given_name, - "family_name": self.family_name, + "given_name": self.given_name, + "family_name": self.family_name, } class ScimUser(object): """ - :ivar id: Unique Twilio user sid - :ivar external_id: External unique resource id defined by provisioning client - :ivar user_name: Unique username, MUST be same as primary email address - :ivar display_name: User friendly display name - :ivar name: - :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. - :ivar active: Indicates whether the user is active - :ivar locale: User's locale - :ivar timezone: User's time zone - :ivar schemas: An array of URIs that indicate the schemas supported for this user resource - :ivar meta: - :ivar detail: A human-readable description of the error - :ivar scim_type: A scimType error code as defined in RFC7644 - :ivar status: Http status code - :ivar code: Twilio-specific error code - :ivar more_info: Link to Error Code References + :ivar id: Unique Twilio user sid + :ivar external_id: External unique resource id defined by provisioning client + :ivar user_name: Unique username, MUST be same as primary email address + :ivar display_name: User friendly display name + :ivar name: + :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + :ivar active: Indicates whether the user is active + :ivar locale: User's locale + :ivar timezone: User's time zone + :ivar schemas: An array of URIs that indicate the schemas supported for this user resource + :ivar meta: + :ivar detail: A human-readable description of the error + :ivar scim_type: A scimType error code as defined in RFC7644 + :ivar status: Http status code + :ivar code: Twilio-specific error code + :ivar more_info: Link to Error Code References """ def __init__(self, payload: Dict[str, Any]): - self.id: Optional[str] = payload.get("id") self.external_id: Optional[str] = payload.get("external_id") self.user_name: Optional[str] = payload.get("user_name") self.display_name: Optional[str] = payload.get("display_name") self.name: Optional[UserList.ScimName] = payload.get("name") - self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get("emails") + self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get( + "emails" + ) self.active: Optional[bool] = payload.get("active") self.locale: Optional[str] = payload.get("locale") self.timezone: Optional[str] = payload.get("timezone") @@ -134,27 +127,28 @@ def __init__(self, payload: Dict[str, Any]): def to_dict(self): return { - - "id": self.id, - "externalId": self.external_id, - "userName": self.user_name, - "displayName": self.display_name, - "name": self.name.to_dict() if self.name is not None else None , - "emails": [emails.to_dict() for emails in self.emails] if self.emails is not None else None, - "active": self.active, - "locale": self.locale, - "timezone": self.timezone, - "schemas": self.schemas, - "meta": self.meta.to_dict() if self.meta is not None else None , - "detail": self.detail, - "scimType": self.scim_type, - "status": self.status, - "code": self.code, - "moreInfo": self.more_info, + "id": self.id, + "externalId": self.external_id, + "userName": self.user_name, + "displayName": self.display_name, + "name": self.name.to_dict() if self.name is not None else None, + "emails": ( + [emails.to_dict() for emails in self.emails] + if self.emails is not None + else None + ), + "active": self.active, + "locale": self.locale, + "timezone": self.timezone, + "schemas": self.schemas, + "meta": self.meta.to_dict() if self.meta is not None else None, + "detail": self.detail, + "scimType": self.scim_type, + "status": self.status, + "code": self.code, + "moreInfo": self.more_info, } - - """ :ivar id: Unique Twilio user sid :ivar external_id: External unique resource id defined by provisioning client @@ -174,10 +168,15 @@ def to_dict(self): :ivar more_info: Link to Error Code References """ - def __init__(self, version: Version, payload: Dict[str, Any], organization_sid: str, id: Optional[str] = None): + def __init__( + self, + version: Version, + payload: Dict[str, Any], + organization_sid: str, + id: Optional[str] = None, + ): super().__init__(version) - self.id: Optional[str] = payload.get("id") self.external_id: Optional[str] = payload.get("externalId") self.user_name: Optional[str] = payload.get("userName") @@ -195,8 +194,7 @@ def __init__(self, version: Version, payload: Dict[str, Any], organization_sid: self.code: Optional[int] = payload.get("code") self.more_info: Optional[str] = payload.get("moreInfo") - - self._solution = { + self._solution = { "organization_sid": organization_sid, "id": id or self.id, } @@ -211,32 +209,35 @@ def _proxy(self) -> "UserContext": :returns: UserContext for this UserInstance """ if self._context is None: - self._context = UserContext(self._version, organization_sid=self._solution['organization_sid'], id=self._solution['id'],) + self._context = UserContext( + self._version, + organization_sid=self._solution["organization_sid"], + id=self._solution["id"], + ) return self._context - - + def delete(self) -> bool: """ Deletes the UserInstance - + :returns: True if delete succeeds, False otherwise """ return self._proxy.delete() + async def delete_async(self) -> bool: """ Asynchronous coroutine that deletes the UserInstance - + :returns: True if delete succeeds, False otherwise """ return await self._proxy.delete_async() - - + def fetch(self) -> "UserInstance": """ Fetch the UserInstance - + :returns: The fetched UserInstance """ @@ -245,79 +246,86 @@ def fetch(self) -> "UserInstance": async def fetch_async(self) -> "UserInstance": """ Asynchronous coroutine to fetch the UserInstance - + :returns: The fetched UserInstance """ return await self._proxy.fetch_async() - - - def update(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> "UserInstance": + + def update( + self, scim_user: ScimUser, if_match: Union[str, object] = values.unset + ) -> "UserInstance": """ Update the UserInstance - - :param scim_user: - :param if_match: + + :param scim_user: + :param if_match: :returns: The updated UserInstance """ - return self._proxy.update(scim_user=scim_user, if_match=if_match, ) + return self._proxy.update( + scim_user=scim_user, + if_match=if_match, + ) - async def update_async(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> "UserInstance": + async def update_async( + self, scim_user: ScimUser, if_match: Union[str, object] = values.unset + ) -> "UserInstance": """ Asynchronous coroutine to update the UserInstance - - :param scim_user: - :param if_match: + + :param scim_user: + :param if_match: :returns: The updated UserInstance """ - return await self._proxy.update_async(scim_user=scim_user, if_match=if_match, ) - + return await self._proxy.update_async( + scim_user=scim_user, + if_match=if_match, + ) + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) - return ''.format(context) + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + class UserContext(InstanceContext): class ScimEmailAddress(object): """ - :ivar primary: Indicates if this email address is the primary one - :ivar value: The actual email address value - :ivar type: The type of email address (e.g., work, home, etc.) + :ivar primary: Indicates if this email address is the primary one + :ivar value: The actual email address value + :ivar type: The type of email address (e.g., work, home, etc.) """ def __init__(self, payload: Dict[str, Any]): - self.primary: Optional[bool] = payload.get("primary") self.value: Optional[str] = payload.get("value") self.type: Optional[str] = payload.get("type") def to_dict(self): return { - - "primary": self.primary, - "value": self.value, - "type": self.type, + "primary": self.primary, + "value": self.value, + "type": self.type, } class ScimMeta(object): """ - :ivar resource_type: Indicates the type of the resource - :ivar created: The date and time when the resource was created in the system - :ivar last_modified: The date and time when the resource was last modified - :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. + :ivar resource_type: Indicates the type of the resource + :ivar created: The date and time when the resource was created in the system + :ivar last_modified: The date and time when the resource was last modified + :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. """ def __init__(self, payload: Dict[str, Any]): - self.resource_type: Optional[str] = payload.get("resource_type") self.created: Optional[datetime] = payload.get("created") self.last_modified: Optional[datetime] = payload.get("last_modified") @@ -325,61 +333,59 @@ def __init__(self, payload: Dict[str, Any]): def to_dict(self): return { - - "resource_type": self.resource_type, - "created": self.created, - "last_modified": self.last_modified, - "version": self.version, + "resource_type": self.resource_type, + "created": self.created, + "last_modified": self.last_modified, + "version": self.version, } class ScimName(object): """ - :ivar given_name: The user's first or given name - :ivar family_name: The user's last or family name + :ivar given_name: The user's first or given name + :ivar family_name: The user's last or family name """ def __init__(self, payload: Dict[str, Any]): - self.given_name: Optional[str] = payload.get("given_name") self.family_name: Optional[str] = payload.get("family_name") def to_dict(self): return { - - "given_name": self.given_name, - "family_name": self.family_name, + "given_name": self.given_name, + "family_name": self.family_name, } class ScimUser(object): """ - :ivar id: Unique Twilio user sid - :ivar external_id: External unique resource id defined by provisioning client - :ivar user_name: Unique username, MUST be same as primary email address - :ivar display_name: User friendly display name - :ivar name: - :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. - :ivar active: Indicates whether the user is active - :ivar locale: User's locale - :ivar timezone: User's time zone - :ivar schemas: An array of URIs that indicate the schemas supported for this user resource - :ivar meta: - :ivar detail: A human-readable description of the error - :ivar scim_type: A scimType error code as defined in RFC7644 - :ivar status: Http status code - :ivar code: Twilio-specific error code - :ivar more_info: Link to Error Code References + :ivar id: Unique Twilio user sid + :ivar external_id: External unique resource id defined by provisioning client + :ivar user_name: Unique username, MUST be same as primary email address + :ivar display_name: User friendly display name + :ivar name: + :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + :ivar active: Indicates whether the user is active + :ivar locale: User's locale + :ivar timezone: User's time zone + :ivar schemas: An array of URIs that indicate the schemas supported for this user resource + :ivar meta: + :ivar detail: A human-readable description of the error + :ivar scim_type: A scimType error code as defined in RFC7644 + :ivar status: Http status code + :ivar code: Twilio-specific error code + :ivar more_info: Link to Error Code References """ def __init__(self, payload: Dict[str, Any]): - self.id: Optional[str] = payload.get("id") self.external_id: Optional[str] = payload.get("external_id") self.user_name: Optional[str] = payload.get("user_name") self.display_name: Optional[str] = payload.get("display_name") self.name: Optional[UserList.ScimName] = payload.get("name") - self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get("emails") + self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get( + "emails" + ) self.active: Optional[bool] = payload.get("active") self.locale: Optional[str] = payload.get("locale") self.timezone: Optional[str] = payload.get("timezone") @@ -393,216 +399,201 @@ def __init__(self, payload: Dict[str, Any]): def to_dict(self): return { - - "id": self.id, - "externalId": self.external_id, - "userName": self.user_name, - "displayName": self.display_name, - "name": self.name.to_dict() if self.name is not None else None , - "emails": [emails.to_dict() for emails in self.emails] if self.emails is not None else None, - "active": self.active, - "locale": self.locale, - "timezone": self.timezone, - "schemas": self.schemas, - "meta": self.meta.to_dict() if self.meta is not None else None , - "detail": self.detail, - "scimType": self.scim_type, - "status": self.status, - "code": self.code, - "moreInfo": self.more_info, + "id": self.id, + "externalId": self.external_id, + "userName": self.user_name, + "displayName": self.display_name, + "name": self.name.to_dict() if self.name is not None else None, + "emails": ( + [emails.to_dict() for emails in self.emails] + if self.emails is not None + else None + ), + "active": self.active, + "locale": self.locale, + "timezone": self.timezone, + "schemas": self.schemas, + "meta": self.meta.to_dict() if self.meta is not None else None, + "detail": self.detail, + "scimType": self.scim_type, + "status": self.status, + "code": self.code, + "moreInfo": self.more_info, } - def __init__(self, version: Version, organization_sid: str, id: str): """ Initialize the UserContext :param version: Version that contains the resource - :param organization_sid: - :param id: + :param organization_sid: + :param id: """ super().__init__(version) - # Path Solution - self._solution = { - 'organization_sid': organization_sid, - 'id': id, + self._solution = { + "organization_sid": organization_sid, + "id": id, } - self._uri = '/{organization_sid}/scim/Users/{id}'.format(**self._solution) - - - + self._uri = "/{organization_sid}/scim/Users/{id}".format(**self._solution) + def delete(self) -> bool: """ Deletes the UserInstance - + :returns: True if delete succeeds, False otherwise """ - headers = values.of({}) - - - + headers["Accept"] = "application/scim+json" - - return self._version.delete(method='DELETE', uri=self._uri, headers=headers) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) async def delete_async(self) -> bool: """ Asynchronous coroutine that deletes the UserInstance - + :returns: True if delete succeeds, False otherwise """ - + headers = values.of({}) - - - + headers["Accept"] = "application/scim+json" - - return await self._version.delete_async(method='DELETE', uri=self._uri, headers=headers) - - + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + def fetch(self) -> UserInstance: """ Fetch the UserInstance - + :returns: The fetched UserInstance """ - headers = values.of({}) - - + headers["Accept"] = "application/scim+json" - - payload = self._version.fetch(method='GET', uri=self._uri , headers=headers) + + payload = self._version.fetch(method="GET", uri=self._uri, headers=headers) return UserInstance( self._version, payload, - organization_sid=self._solution['organization_sid'], - id=self._solution['id'], - + organization_sid=self._solution["organization_sid"], + id=self._solution["id"], ) async def fetch_async(self) -> UserInstance: """ Asynchronous coroutine to fetch the UserInstance - + :returns: The fetched UserInstance """ - headers = values.of({}) - - + headers["Accept"] = "application/scim+json" - - payload = await self._version.fetch_async(method='GET', uri=self._uri , headers=headers) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, headers=headers + ) return UserInstance( self._version, payload, - organization_sid=self._solution['organization_sid'], - id=self._solution['id'], - + organization_sid=self._solution["organization_sid"], + id=self._solution["id"], ) - - - def update(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> UserInstance: + + def update( + self, scim_user: ScimUser, if_match: Union[str, object] = values.unset + ) -> UserInstance: """ Update the UserInstance - - :param scim_user: - :param if_match: + + :param scim_user: + :param if_match: :returns: The updated UserInstance """ data = scim_user.to_dict() - + headers = values.of({}) - - - if not (if_match is values.unset or (isinstance(if_match, str) and not if_match)): - headers['If-Match'] = if_match - + + if not ( + if_match is values.unset or (isinstance(if_match, str) and not if_match) + ): + headers["If-Match"] = if_match + headers["Content-Type"] = "application/json" - + headers["Content-Type"] = "application/scim+json" - - + headers["Accept"] = "application/scim+json" - - payload = self._version.update(method='PUT', uri=self._uri, data=data, headers=headers) + payload = self._version.update( + method="PUT", uri=self._uri, data=data, headers=headers + ) return UserInstance( self._version, payload, - organization_sid=self._solution['organization_sid'], - id=self._solution['id'] + organization_sid=self._solution["organization_sid"], + id=self._solution["id"], ) - async def update_async(self, scim_user: ScimUser, if_match: Union[str, object]=values.unset) -> UserInstance: + async def update_async( + self, scim_user: ScimUser, if_match: Union[str, object] = values.unset + ) -> UserInstance: """ Asynchronous coroutine to update the UserInstance - - :param scim_user: - :param if_match: + + :param scim_user: + :param if_match: :returns: The updated UserInstance """ data = scim_user.to_dict() - + headers = values.of({}) - - - if not (if_match is values.unset or (isinstance(if_match, str) and not if_match)): - headers['If-Match'] = if_match - - + + if not ( + if_match is values.unset or (isinstance(if_match, str) and not if_match) + ): + headers["If-Match"] = if_match + headers["Content-Type"] = "application/json" - + headers["Content-Type"] = "application/scim+json" - - + headers["Accept"] = "application/scim+json" - - payload = await self._version.update_async(method='PUT', uri=self._uri, data=data, headers=headers) + payload = await self._version.update_async( + method="PUT", uri=self._uri, data=data, headers=headers + ) return UserInstance( self._version, payload, - organization_sid=self._solution['organization_sid'], - id=self._solution['id'] + organization_sid=self._solution["organization_sid"], + id=self._solution["id"], ) - - + def __repr__(self) -> str: """ Provide a friendly representation :returns: Machine friendly representation """ - context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) - return ''.format(context) - - - - - - - - - + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) class UserPage(Page): @@ -613,7 +604,9 @@ def get_instance(self, payload: Dict[str, Any]) -> UserInstance: :param payload: Payload response from the API """ - return UserInstance(self._version, payload, organization_sid=self._solution["organization_sid"]) + return UserInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) def __repr__(self) -> str: """ @@ -624,44 +617,38 @@ def __repr__(self) -> str: return "" - - - class UserList(ListResource): - + class ScimEmailAddress(object): """ - :ivar primary: Indicates if this email address is the primary one - :ivar value: The actual email address value - :ivar type: The type of email address (e.g., work, home, etc.) + :ivar primary: Indicates if this email address is the primary one + :ivar value: The actual email address value + :ivar type: The type of email address (e.g., work, home, etc.) """ def __init__(self, payload: Dict[str, Any]): - self.primary: Optional[bool] = payload.get("primary") self.value: Optional[str] = payload.get("value") self.type: Optional[str] = payload.get("type") def to_dict(self): return { - - "primary": self.primary, - "value": self.value, - "type": self.type, + "primary": self.primary, + "value": self.value, + "type": self.type, } class ScimMeta(object): """ - :ivar resource_type: Indicates the type of the resource - :ivar created: The date and time when the resource was created in the system - :ivar last_modified: The date and time when the resource was last modified - :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. + :ivar resource_type: Indicates the type of the resource + :ivar created: The date and time when the resource was created in the system + :ivar last_modified: The date and time when the resource was last modified + :ivar version: A version identifier for the resource. This can be used to manage resource versioning and concurrency control. """ def __init__(self, payload: Dict[str, Any]): - self.resource_type: Optional[str] = payload.get("resource_type") self.created: Optional[datetime] = payload.get("created") self.last_modified: Optional[datetime] = payload.get("last_modified") @@ -669,61 +656,59 @@ def __init__(self, payload: Dict[str, Any]): def to_dict(self): return { - - "resource_type": self.resource_type, - "created": self.created, - "last_modified": self.last_modified, - "version": self.version, + "resource_type": self.resource_type, + "created": self.created, + "last_modified": self.last_modified, + "version": self.version, } class ScimName(object): """ - :ivar given_name: The user's first or given name - :ivar family_name: The user's last or family name + :ivar given_name: The user's first or given name + :ivar family_name: The user's last or family name """ def __init__(self, payload: Dict[str, Any]): - self.given_name: Optional[str] = payload.get("given_name") self.family_name: Optional[str] = payload.get("family_name") def to_dict(self): return { - - "given_name": self.given_name, - "family_name": self.family_name, + "given_name": self.given_name, + "family_name": self.family_name, } class ScimUser(object): """ - :ivar id: Unique Twilio user sid - :ivar external_id: External unique resource id defined by provisioning client - :ivar user_name: Unique username, MUST be same as primary email address - :ivar display_name: User friendly display name - :ivar name: - :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. - :ivar active: Indicates whether the user is active - :ivar locale: User's locale - :ivar timezone: User's time zone - :ivar schemas: An array of URIs that indicate the schemas supported for this user resource - :ivar meta: - :ivar detail: A human-readable description of the error - :ivar scim_type: A scimType error code as defined in RFC7644 - :ivar status: Http status code - :ivar code: Twilio-specific error code - :ivar more_info: Link to Error Code References + :ivar id: Unique Twilio user sid + :ivar external_id: External unique resource id defined by provisioning client + :ivar user_name: Unique username, MUST be same as primary email address + :ivar display_name: User friendly display name + :ivar name: + :ivar emails: Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + :ivar active: Indicates whether the user is active + :ivar locale: User's locale + :ivar timezone: User's time zone + :ivar schemas: An array of URIs that indicate the schemas supported for this user resource + :ivar meta: + :ivar detail: A human-readable description of the error + :ivar scim_type: A scimType error code as defined in RFC7644 + :ivar status: Http status code + :ivar code: Twilio-specific error code + :ivar more_info: Link to Error Code References """ def __init__(self, payload: Dict[str, Any]): - self.id: Optional[str] = payload.get("id") self.external_id: Optional[str] = payload.get("external_id") self.user_name: Optional[str] = payload.get("user_name") self.display_name: Optional[str] = payload.get("display_name") self.name: Optional[UserList.ScimName] = payload.get("name") - self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get("emails") + self.emails: Optional[List[UserList.ScimEmailAddress]] = payload.get( + "emails" + ) self.active: Optional[bool] = payload.get("active") self.locale: Optional[str] = payload.get("locale") self.timezone: Optional[str] = payload.get("timezone") @@ -737,100 +722,98 @@ def __init__(self, payload: Dict[str, Any]): def to_dict(self): return { - - "id": self.id, - "externalId": self.external_id, - "userName": self.user_name, - "displayName": self.display_name, - "name": self.name.to_dict() if self.name is not None else None , - "emails": [emails.to_dict() for emails in self.emails] if self.emails is not None else None, - "active": self.active, - "locale": self.locale, - "timezone": self.timezone, - "schemas": self.schemas, - "meta": self.meta.to_dict() if self.meta is not None else None , - "detail": self.detail, - "scimType": self.scim_type, - "status": self.status, - "code": self.code, - "moreInfo": self.more_info, + "id": self.id, + "externalId": self.external_id, + "userName": self.user_name, + "displayName": self.display_name, + "name": self.name.to_dict() if self.name is not None else None, + "emails": ( + [emails.to_dict() for emails in self.emails] + if self.emails is not None + else None + ), + "active": self.active, + "locale": self.locale, + "timezone": self.timezone, + "schemas": self.schemas, + "meta": self.meta.to_dict() if self.meta is not None else None, + "detail": self.detail, + "scimType": self.scim_type, + "status": self.status, + "code": self.code, + "moreInfo": self.more_info, } - def __init__(self, version: Version, organization_sid: str): """ Initialize the UserList :param version: Version that contains the resource - :param organization_sid: - + :param organization_sid: + """ super().__init__(version) - # Path Solution - self._solution = { 'organization_sid': organization_sid, } - self._uri = '/{organization_sid}/scim/Users'.format(**self._solution) - - - - - - + self._solution = { + "organization_sid": organization_sid, + } + self._uri = "/{organization_sid}/scim/Users".format(**self._solution) + def create(self, scim_user: ScimUser) -> UserInstance: """ Create the UserInstance - :param scim_user: - + :param scim_user: + :returns: The created UserInstance """ data = scim_user.to_dict() - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Content-Type"] = "application/json" - + headers["Content-Type"] = "application/scim+json" - - + headers["Accept"] = "application/scim+json" - - - payload = self._version.create(method='POST', uri=self._uri, data=data, headers=headers) - return UserInstance(self._version, payload, organization_sid=self._solution['organization_sid']) + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) async def create_async(self, scim_user: ScimUser) -> UserInstance: """ Asynchronously create the UserInstance - :param scim_user: - + :param scim_user: + :returns: The created UserInstance """ data = scim_user.to_dict() - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Content-Type"] = "application/json" - + headers["Content-Type"] = "application/scim+json" - - + headers["Accept"] = "application/scim+json" - - - payload = await self._version.create_async(method='POST', uri=self._uri, data=data, headers=headers) - - return UserInstance(self._version, payload, organization_sid=self._solution['organization_sid']) - - - def stream(self, + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, payload, organization_sid=self._solution["organization_sid"] + ) + + def stream( + self, filter: Union[str, object] = values.unset, limit: Optional[int] = None, page_size: Optional[int] = None, @@ -840,8 +823,8 @@ def stream(self, This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - - :param str filter: + + :param str filter: :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -852,14 +835,12 @@ def stream(self, :returns: Generator that will yield up to limit results """ limits = self._version.read_limits(limit, page_size) - page = self.page( - filter=filter, - page_size=limits['page_size'] - ) + page = self.page(filter=filter, page_size=limits["page_size"]) - return self._version.stream(page, limits['limit']) + return self._version.stream(page, limits["limit"]) - async def stream_async(self, + async def stream_async( + self, filter: Union[str, object] = values.unset, limit: Optional[int] = None, page_size: Optional[int] = None, @@ -869,8 +850,8 @@ async def stream_async(self, This operation lazily loads records as efficiently as possible until the limit is reached. The results are returned as a generator, so this operation is memory efficient. - - :param str filter: + + :param str filter: :param limit: Upper limit for the number of records to return. stream() guarantees to never return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -881,14 +862,12 @@ async def stream_async(self, :returns: Generator that will yield up to limit results """ limits = self._version.read_limits(limit, page_size) - page = await self.page_async( - filter=filter, - page_size=limits['page_size'] - ) + page = await self.page_async(filter=filter, page_size=limits["page_size"]) - return self._version.stream_async(page, limits['limit']) + return self._version.stream_async(page, limits["limit"]) - def list(self, + def list( + self, filter: Union[str, object] = values.unset, limit: Optional[int] = None, page_size: Optional[int] = None, @@ -897,8 +876,8 @@ def list(self, Lists UserInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - - :param str filter: + + :param str filter: :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -908,13 +887,16 @@ def list(self, :returns: list that will contain up to limit results """ - return list(self.stream( - filter=filter, - limit=limit, - page_size=page_size, - )) + return list( + self.stream( + filter=filter, + limit=limit, + page_size=page_size, + ) + ) - async def list_async(self, + async def list_async( + self, filter: Union[str, object] = values.unset, limit: Optional[int] = None, page_size: Optional[int] = None, @@ -923,8 +905,8 @@ async def list_async(self, Asynchronously lists UserInstance records from the API as a list. Unlike stream(), this operation is eager and will load `limit` records into memory before returning. - - :param str filter: + + :param str filter: :param limit: Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit :param page_size: Number of records to fetch per request, when not set will use @@ -934,13 +916,17 @@ async def list_async(self, :returns: list that will contain up to limit results """ - return [record async for record in await self.stream_async( - filter=filter, - limit=limit, - page_size=page_size, - )] - - def page(self, + return [ + record + async for record in await self.stream_async( + filter=filter, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, filter: Union[str, object] = values.unset, page_token: Union[str, object] = values.unset, page_number: Union[int, object] = values.unset, @@ -949,33 +935,34 @@ def page(self, """ Retrieve a single page of UserInstance records from the API. Request is executed immediately - - :param filter: + + :param filter: :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of UserInstance """ - data = values.of({ - 'filter': filter, - 'PageToken': page_token, - 'Page': page_number, - 'PageSize': page_size, - }) - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - + data = values.of( + { + "filter": filter, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Accept"] = "application/scim+json" - - response = self._version.page(method='GET', uri=self._uri, params=data, headers=headers) + response = self._version.page( + method="GET", uri=self._uri, params=data, headers=headers + ) return UserPage(self._version, response, self._solution) - async def page_async(self, + async def page_async( + self, filter: Union[str, object] = values.unset, page_token: Union[str, object] = values.unset, page_number: Union[int, object] = values.unset, @@ -984,30 +971,30 @@ async def page_async(self, """ Asynchronously retrieve a single page of UserInstance records from the API. Request is executed immediately - - :param filter: + + :param filter: :param page_token: PageToken provided by the API :param page_number: Page Number, this value is simply for client state :param page_size: Number of records to return, defaults to 50 :returns: Page of UserInstance """ - data = values.of({ - 'filter': filter, - 'PageToken': page_token, - 'Page': page_number, - 'PageSize': page_size, - }) - - headers = values.of({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - - + data = values.of( + { + "filter": filter, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + headers = values.of({"Content-Type": "application/x-www-form-urlencoded"}) + headers["Accept"] = "application/scim+json" - - response = await self._version.page_async(method='GET', uri=self._uri, params=data, headers=headers) + response = await self._version.page_async( + method="GET", uri=self._uri, params=data, headers=headers + ) return UserPage(self._version, response, self._solution) def get_page(self, target_url: str) -> UserPage: @@ -1019,10 +1006,7 @@ def get_page(self, target_url: str) -> UserPage: :returns: Page of UserInstance """ - response = self._version.domain.twilio.request( - 'GET', - target_url - ) + response = self._version.domain.twilio.request("GET", target_url) return UserPage(self._version, response, self._solution) async def get_page_async(self, target_url: str) -> UserPage: @@ -1034,29 +1018,28 @@ async def get_page_async(self, target_url: str) -> UserPage: :returns: Page of UserInstance """ - response = await self._version.domain.twilio.request_async( - 'GET', - target_url - ) + response = await self._version.domain.twilio.request_async("GET", target_url) return UserPage(self._version, response, self._solution) - - def get(self, id: str) -> UserContext: """ Constructs a UserContext - - :param id: + + :param id: """ - return UserContext(self._version, organization_sid=self._solution['organization_sid'], id=id) + return UserContext( + self._version, organization_sid=self._solution["organization_sid"], id=id + ) def __call__(self, id: str) -> UserContext: """ Constructs a UserContext - - :param id: + + :param id: """ - return UserContext(self._version, organization_sid=self._solution['organization_sid'], id=id) + return UserContext( + self._version, organization_sid=self._solution["organization_sid"], id=id + ) def __repr__(self) -> str: """ @@ -1064,5 +1047,4 @@ def __repr__(self) -> str: :returns: Machine friendly representation """ - return '' - + return "" From eafd8dd11caa98c96fdfa538afcfe23e51ebd106 Mon Sep 17 00:00:00 2001 From: AsabuHere Date: Thu, 12 Dec 2024 15:07:09 +0530 Subject: [PATCH 33/33] Adding examples and pushing client token manager and cred provider --- README.md | 7 ++++ examples/organization_api_calls.py | 28 +++++++++++++ examples/public_oauth.py | 35 ++++++++++++++++ .../credential/client_credential_provider.py | 28 +++++++++++++ twilio/http/client_token_manager.py | 41 +++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 examples/organization_api_calls.py create mode 100644 examples/public_oauth.py create mode 100644 twilio/credential/client_credential_provider.py create mode 100644 twilio/http/client_token_manager.py diff --git a/README.md b/README.md index 50cc5509e..c3afaf1fa 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,13 @@ After a brief delay, you will receive the text message on your phone. > **Warning** > It's okay to hardcode your credentials when testing locally, but you should use environment variables to keep them secret before committing any code or deploying to production. Check out [How to Set Environment Variables](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html) for more information. +## OAuth Feature for Twilio APIs +We are introducing Client Credentials Flow-based OAuth 2.0 authentication. This feature is currently in beta and its implementation is subject to change. + +API examples [here](https://github.com/twilio/twilio-python/blob/main/examples/public_oauth.py) + +Organisation API examples [here](https://github.com/twilio/twilio-python/blob/main/examples/organization_api_calls.py) + ## Use the helper library ### API Credentials diff --git a/examples/organization_api_calls.py b/examples/organization_api_calls.py new file mode 100644 index 000000000..e94ef9bbe --- /dev/null +++ b/examples/organization_api_calls.py @@ -0,0 +1,28 @@ +import os + +from twilio.rest import Client +from twilio.credential.orgs_credential_provider import OrgsCredentialProvider + +API_KEY = os.environ.get("TWILIO_API_KEY") +API_SECRET = os.environ.get("TWILIO_API_SECRET") +ACCOUNT_SID = os.environ.get("TWILIO_ACCOUNT_SID") +CLIENT_ID = os.environ.get("TWILIO_CLIENT_ID") +CLIENT_SECRET = os.environ.get("TWILIO_CLIENT_SECRET") +ORGS_SID = os.environ.get("TWILIO_ORGS_SID") + + +def example(self=None): + """ + Some example usage of fetching accounts within an organization + """ + self.client = Client( + username=API_KEY, + password=API_SECRET, + account_sid=ACCOUNT_SID, + credential_provider= OrgsCredentialProvider(CLIENT_ID, CLIENT_SECRET) + ) + accounts = self.client.preview_iam.organization(organization_sid=ORGS_SID).accounts.stream() + self.assertIsNotNone(accounts) + +if __name__ == "__main__": + example() diff --git a/examples/public_oauth.py b/examples/public_oauth.py new file mode 100644 index 000000000..22065738a --- /dev/null +++ b/examples/public_oauth.py @@ -0,0 +1,35 @@ +import os + +from twilio.rest import Client +from twilio.credential.client_credential_provider import ClientCredentialProvider + +API_KEY = os.environ.get("TWILIO_API_KEY") +API_SECRET = os.environ.get("TWILIO_API_SECRET") +ACCOUNT_SID = os.environ.get("TWILIO_ACCOUNT_SID") +CLIENT_ID = os.environ.get("TWILIO_CLIENT_ID") +CLIENT_SECRET = os.environ.get("TWILIO_CLIENT_SECRET") +ORGS_SID = os.environ.get("TWILIO_ORGS_SID") +FROM_NUMBER = os.environ.get("TWILIO_FROM_NUMBER") +TO_NUMBER = os.environ.get("TWILIO_TO_NUMBER") + + +def example(self=None): + """ + Some example usage of fetching accounts within an organization + """ + self.client = Client( + username=API_KEY, + password=API_SECRET, + account_sid=ACCOUNT_SID, + credential_provider= ClientCredentialProvider(CLIENT_ID, CLIENT_SECRET) + ) + msg = self.client.messages.create( + to=self.to_number, from_=self.from_number, body="hello world" + ) + self.assertEqual(msg.to, self.to_number) + self.assertEqual(msg.from_, self.from_number) + self.assertEqual(msg.body, "hello world") + self.assertIsNotNone(msg.sid) + +if __name__ == "__main__": + example() diff --git a/twilio/credential/client_credential_provider.py b/twilio/credential/client_credential_provider.py new file mode 100644 index 000000000..d756fd088 --- /dev/null +++ b/twilio/credential/client_credential_provider.py @@ -0,0 +1,28 @@ +from twilio.http.orgs_token_manager import OrgTokenManager +from twilio.base.exceptions import TwilioException +from twilio.credential.credential_provider import CredentialProvider +from twilio.auth_strategy.auth_type import AuthType +from twilio.auth_strategy.token_auth_strategy import TokenAuthStrategy + + +class ClientCredentialProvider(CredentialProvider): + def __init__(self, client_id: str, client_secret: str, token_manager=None): + super().__init__(AuthType.CLIENT_CREDENTIALS) + + if client_id is None or client_secret is None: + raise TwilioException("Client id and Client secret are mandatory") + + self.grant_type = "client_credentials" + self.client_id = client_id + self.client_secret = client_secret + self.token_manager = token_manager + self.auth_strategy = 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 + ) + if self.auth_strategy is None: + self.auth_strategy = TokenAuthStrategy(self.token_manager) + return self.auth_strategy diff --git a/twilio/http/client_token_manager.py b/twilio/http/client_token_manager.py new file mode 100644 index 000000000..d65acb2ba --- /dev/null +++ b/twilio/http/client_token_manager.py @@ -0,0 +1,41 @@ +from twilio.http.token_manager import TokenManager +from twilio.rest import Client + + +class ClientTokenManager(TokenManager): + """ + Client Token Manager + """ + + def __init__( + self, + grant_type: str, + client_id: str, + client_secret: str, + code: str = None, + redirect_uri: str = None, + audience: str = None, + refreshToken: str = None, + scope: str = None, + ): + self.grant_type = grant_type + self.client_id = client_id + self.client_secret = client_secret + self.code = code + self.redirect_uri = redirect_uri + self.audience = audience + self.refreshToken = refreshToken + self.scope = scope + self.client = Client() + + def fetch_access_token(self): + token_instance = self.client.preview_iam.v1.token.create( + grant_type=self.grant_type, + client_id=self.client_id, + client_secret=self.client_secret, + code=self.code, + redirect_uri=self.redirect_uri, + audience=self.audience, + scope=self.scope, + ) + return token_instance.access_token