From 7b1061bf55adf583731d5cac73011e785ff606fb Mon Sep 17 00:00:00 2001 From: Devanshusisodiya Date: Mon, 27 Jan 2025 02:58:02 +0530 Subject: [PATCH 1/4] fix: auth schemes --- python/composio/cli/add.py | 8 ++++++-- python/composio/client/collections.py | 26 ++++++++++++++++++++++---- python/composio/tools/toolset.py | 23 ++++++++++++++++------- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/python/composio/cli/add.py b/python/composio/cli/add.py index 0c084e603ca..7257d54a00d 100644 --- a/python/composio/cli/add.py +++ b/python/composio/cli/add.py @@ -238,12 +238,16 @@ def add_integration( AuthSchemeType, click.prompt( "Select auth mode: ", - type=click.Choice(choices=list(auth_modes)), + type=click.Choice(choices=t.cast(t.List[str], auth_modes)), ), ) auth_scheme = auth_modes[auth_mode] - if auth_mode.lower() in ("basic", "api_key", "bearer_token"): + if auth_mode is not None and auth_mode.lower() in ( + "basic", + "api_key", + "bearer_token", + ): return _handle_basic_auth( entity=entity, app_name=name, diff --git a/python/composio/client/collections.py b/python/composio/client/collections.py index 622608203d5..d771c36aeb0 100644 --- a/python/composio/client/collections.py +++ b/python/composio/client/collections.py @@ -293,8 +293,10 @@ class AuthSchemeField(BaseModel): class AppAuthScheme(BaseModel): """App authenticatio scheme.""" - scheme_name: str - auth_mode: AuthSchemeType + scheme_name: t.Optional[str] = None + name: t.Optional[str] = None + auth_mode: t.Optional[AuthSchemeType] = None + mode: t.Optional[str] = None fields: t.List[AuthSchemeField] proxy: t.Optional[t.Dict] = None @@ -342,8 +344,18 @@ def get(self) -> t.List[AppModel]: def get(self, name: t.Optional[str] = None) -> AppModel: """Get a specific app.""" - def get(self, name: t.Optional[str] = None) -> t.Union[AppModel, t.List[AppModel]]: + def get( + self, + name: t.Optional[str] = None, + include_local: bool = False, + additional_fields: t.Optional[t.List[str]] = None, + ) -> t.Union[AppModel, t.List[AppModel]]: """Get apps.""" + queries = {} + if include_local: + queries["includeLocal"] = "true" + if additional_fields is not None and len(additional_fields) > 0: + queries["additionalFields"] = ",".join(additional_fields) if name is not None: return self.model( **self._raise_if_required( @@ -353,7 +365,13 @@ def get(self, name: t.Optional[str] = None) -> t.Union[AppModel, t.List[AppModel ).json() ) - return super().get(queries={}) + apps: t.Union[AppModel, t.List[AppModel]] = super().get(queries=queries) + for app in apps: + if app.auth_schemes is not None: # type: ignore + for auth_scheme in app.auth_schemes: # type: ignore + if auth_scheme.mode is not None: + auth_scheme.auth_mode = t.cast(AuthSchemeType, auth_scheme.mode) + return apps class TypeModel(BaseModel): diff --git a/python/composio/tools/toolset.py b/python/composio/tools/toolset.py index 9bf95649281..dab1b968cb2 100644 --- a/python/composio/tools/toolset.py +++ b/python/composio/tools/toolset.py @@ -867,10 +867,17 @@ def get_app(self, app: AppType) -> AppModel: def get_apps( self, + name: t.Optional[str] = None, no_auth: t.Optional[bool] = None, - include_local: bool = True, + include_local: bool = False, + additional_fields: t.List[str] = ["auth_schemes"], ) -> t.List[AppModel]: - apps = self.client.apps.get() + # added type ignore since method overload was not being referenced + apps = self.client.apps.get( + name=str(name) if name else None, + include_local=include_local, + additional_fields=additional_fields, + ) # type: ignore if no_auth is not None: apps = [a for a in apps if a.no_auth is no_auth] @@ -1051,8 +1058,9 @@ def get_expected_params_for_user( # without user inputs to create an integratuib, if yes then create # an integration and return params from there. for scheme in app_data.auth_schemes or []: - if auth_scheme is not None and auth_scheme != scheme.auth_mode.upper(): - continue + if scheme.auth_mode is not None: + if auth_scheme is not None and auth_scheme != scheme.auth_mode.upper(): + continue if self._can_use_auth_scheme_without_user_input( scheme=scheme, app=app_data ): @@ -1083,8 +1091,9 @@ def fetch_expected_integration_params( ) -> t.List[AuthSchemeField]: """Fetch expected integration params for creating an integration.""" for scheme in app.auth_schemes or []: - if auth_scheme != scheme.auth_mode.upper(): - continue + if scheme.auth_mode is not None: + if auth_scheme != scheme.auth_mode.upper(): + continue return [f for f in scheme.fields if not f.expected_from_customer] raise ComposioSDKError( message=f"{app.name!r} does not support {auth_scheme!r} auth scheme" @@ -1154,7 +1163,7 @@ def initiate_connection( ).id except NoItemsFound: auth_config, use_composio_auth = self._validate_auth_config( - app, auth_scheme, auth_config + app, t.cast(AuthSchemeType, auth_scheme), auth_config ) integration = self.create_integration( app=app, From 277eae9bb5efd366fceb6d3a28c0ee175ec6c711 Mon Sep 17 00:00:00 2001 From: Devanshusisodiya Date: Mon, 27 Jan 2025 03:44:18 +0530 Subject: [PATCH 2/4] test: added test --- python/tests/test_tools/test_toolset.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/tests/test_tools/test_toolset.py b/python/tests/test_tools/test_toolset.py index b9a728b618b..488fb0aa87d 100644 --- a/python/tests/test_tools/test_toolset.py +++ b/python/tests/test_tools/test_toolset.py @@ -11,6 +11,7 @@ from pydantic import BaseModel, Field from composio import Action, App +from composio.client.collections import AUTH_SCHEMES from composio.exceptions import ApiKeyNotProvidedError, ComposioSDKError from composio.tools.base.abs import action_registry, tool_registry from composio.tools.base.runtime import action as custom_action @@ -67,6 +68,17 @@ def test_uninitialize_app() -> None: ComposioToolSet().get_action_schemas(actions=[Action.ATTIO_UPDATE_A_LIST]) +def test_get_apps() -> None: + toolset = ComposioToolSet() + apps = toolset.get_apps() + for app in apps: + if app.no_auth is False: + auth_app = app + break + if auth_app.auth_schemes: + for auth_scheme in auth_app.auth_schemes: + assert auth_scheme.auth_mode in AUTH_SCHEMES + class TestValidateTools: toolset: ComposioToolSet package = "somepackage1" From 8524a9efdc03ceb2c950dbf41eefb853184c2a95 Mon Sep 17 00:00:00 2001 From: Devanshusisodiya Date: Mon, 27 Jan 2025 12:44:35 +0530 Subject: [PATCH 3/4] chore: fmt chk --- python/tests/test_tools/test_toolset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/tests/test_tools/test_toolset.py b/python/tests/test_tools/test_toolset.py index 488fb0aa87d..3050015aae3 100644 --- a/python/tests/test_tools/test_toolset.py +++ b/python/tests/test_tools/test_toolset.py @@ -79,6 +79,7 @@ def test_get_apps() -> None: for auth_scheme in auth_app.auth_schemes: assert auth_scheme.auth_mode in AUTH_SCHEMES + class TestValidateTools: toolset: ComposioToolSet package = "somepackage1" From ec3e8a4fbea408b179f4b1eab70a4a80aed61f9f Mon Sep 17 00:00:00 2001 From: Devanshusisodiya Date: Tue, 28 Jan 2025 19:19:29 +0530 Subject: [PATCH 4/4] fix: added exception handling --- python/composio/client/collections.py | 15 +++++--------- python/composio/tools/toolset.py | 28 +++++++++++++-------------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/python/composio/client/collections.py b/python/composio/client/collections.py index d771c36aeb0..6c7d4176247 100644 --- a/python/composio/client/collections.py +++ b/python/composio/client/collections.py @@ -347,15 +347,10 @@ def get(self, name: t.Optional[str] = None) -> AppModel: def get( self, name: t.Optional[str] = None, - include_local: bool = False, - additional_fields: t.Optional[t.List[str]] = None, ) -> t.Union[AppModel, t.List[AppModel]]: """Get apps.""" - queries = {} - if include_local: - queries["includeLocal"] = "true" - if additional_fields is not None and len(additional_fields) > 0: - queries["additionalFields"] = ",".join(additional_fields) + queries = {"additionalFields": "auth_schemes,test_connectors"} + if name is not None: return self.model( **self._raise_if_required( @@ -365,10 +360,10 @@ def get( ).json() ) - apps: t.Union[AppModel, t.List[AppModel]] = super().get(queries=queries) + apps = super().get(queries=queries) for app in apps: - if app.auth_schemes is not None: # type: ignore - for auth_scheme in app.auth_schemes: # type: ignore + if app.auth_schemes is not None: + for auth_scheme in app.auth_schemes: if auth_scheme.mode is not None: auth_scheme.auth_mode = t.cast(AuthSchemeType, auth_scheme.mode) return apps diff --git a/python/composio/tools/toolset.py b/python/composio/tools/toolset.py index dab1b968cb2..b7e32628ed4 100644 --- a/python/composio/tools/toolset.py +++ b/python/composio/tools/toolset.py @@ -867,17 +867,11 @@ def get_app(self, app: AppType) -> AppModel: def get_apps( self, - name: t.Optional[str] = None, no_auth: t.Optional[bool] = None, - include_local: bool = False, - additional_fields: t.List[str] = ["auth_schemes"], + include_local: bool = True, ) -> t.List[AppModel]: # added type ignore since method overload was not being referenced - apps = self.client.apps.get( - name=str(name) if name else None, - include_local=include_local, - additional_fields=additional_fields, - ) # type: ignore + apps = self.client.apps.get() if no_auth is not None: apps = [a for a in apps if a.no_auth is no_auth] @@ -1058,9 +1052,12 @@ def get_expected_params_for_user( # without user inputs to create an integratuib, if yes then create # an integration and return params from there. for scheme in app_data.auth_schemes or []: - if scheme.auth_mode is not None: - if auth_scheme is not None and auth_scheme != scheme.auth_mode.upper(): - continue + if scheme.auth_mode is None: + raise ComposioSDKError( + message=f"No auth scheme found for app `{app_data.name}`" + ) + if auth_scheme is not None and auth_scheme != scheme.auth_mode.upper(): + continue if self._can_use_auth_scheme_without_user_input( scheme=scheme, app=app_data ): @@ -1091,9 +1088,12 @@ def fetch_expected_integration_params( ) -> t.List[AuthSchemeField]: """Fetch expected integration params for creating an integration.""" for scheme in app.auth_schemes or []: - if scheme.auth_mode is not None: - if auth_scheme != scheme.auth_mode.upper(): - continue + if scheme.auth_mode is None: + raise ComposioSDKError( + message=f"No auth scheme found for app `{app.name}`" + ) + if auth_scheme != scheme.auth_mode.upper(): + continue return [f for f in scheme.fields if not f.expected_from_customer] raise ComposioSDKError( message=f"{app.name!r} does not support {auth_scheme!r} auth scheme"