Skip to content

Commit

Permalink
Merge branch 'main' into fix/connector_submit_handling
Browse files Browse the repository at this point in the history
  • Loading branch information
johnyrahul authored Jul 18, 2024
2 parents ca52841 + f705ee6 commit 0d40e67
Show file tree
Hide file tree
Showing 17 changed files with 178 additions and 131 deletions.
17 changes: 6 additions & 11 deletions backend/connector_processor/connector_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
InValidConnectorId,
InValidConnectorMode,
OAuthTimeOut,
TestConnectorException,
TestConnectorInputException,
TestConnectorInputError,
)

from unstract.connectors.base import UnstractConnector
Expand Down Expand Up @@ -100,15 +99,15 @@ def get_all_supported_connectors(
return supported_connectors

@staticmethod
def test_connectors(connector_id: str, cred_string: dict[str, Any]) -> bool:
def test_connectors(connector_id: str, credentials: dict[str, Any]) -> bool:
logger.info(f"Testing connector: {connector_id}")
connector: dict[str, Any] = fetch_connectors_by_key_value(
ConnectorKeys.ID, connector_id
)[0]
if connector.get(ConnectorKeys.OAUTH):
try:
oauth_key = cred_string.get(ConnectorAuthKey.OAUTH_KEY)
cred_string = ConnectorAuthHelper.get_oauth_creds_from_cache(
oauth_key = credentials.get(ConnectorAuthKey.OAUTH_KEY)
credentials = ConnectorAuthHelper.get_oauth_creds_from_cache(
cache_key=oauth_key, delete_key=False
)
except Exception as exc:
Expand All @@ -120,17 +119,13 @@ def test_connectors(connector_id: str, cred_string: dict[str, Any]) -> bool:

try:
connector_impl = Connectorkit().get_connector_by_id(
connector_id, cred_string
connector_id, credentials
)
test_result = connector_impl.test_credentials()
logger.info(f"{connector_id} test result: {test_result}")
return test_result
except ConnectorError as e:
logger.error(f"Error while testing {connector_id}: {e}")
raise TestConnectorInputException(core_err=e)
except Exception as e:
logger.error(f"Error while testing {connector_id}: {e}")
raise TestConnectorException
raise TestConnectorInputError(core_err=e)

def get_connector_data_with_key(connector_id: str, key_value: str) -> Any:
"""Generic Function to get connector data with provided key."""
Expand Down
4 changes: 2 additions & 2 deletions backend/connector_processor/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class JSONParseException(APIException):

class OAuthTimeOut(APIException):
status_code = 408
default_detail = "Timed Out. Please re authenticate."
default_detail = "Timed out. Please re-authenticate."


class InternalServiceError(APIException):
Expand All @@ -44,7 +44,7 @@ class TestConnectorException(APIException):
default_detail = "Error while testing connector."


class TestConnectorInputException(UnstractBaseException):
class TestConnectorInputError(UnstractBaseException):
def __init__(self, core_err: ConnectorError) -> None:
super().__init__(detail=core_err.message, core_err=core_err)
self.default_detail = core_err.message
Expand Down
4 changes: 2 additions & 2 deletions backend/connector_processor/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ def test(self, request: Request) -> Response:
"""Tests the connector against the credentials passed."""
serializer: TestConnectorSerializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
connector_id = serializer.validated_data.get(ConnectorKeys.CONNECTOR_ID)
connector_id = serializer.validated_data.get(CIKey.CONNECTOR_ID)
cred_string = serializer.validated_data.get(CIKey.CONNECTOR_METADATA)
test_result = ConnectorProcessor.test_connectors(
connector_id=connector_id, cred_string=cred_string
connector_id=connector_id, credentials=cred_string
)
return Response(
{ConnectorKeys.IS_VALID: test_result},
Expand Down
147 changes: 64 additions & 83 deletions backend/pdm.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ def update_or_create_psr_tool(
obj, created = PromptStudioRegistry.objects.update_or_create(
custom_tool=custom_tool,
created_by=custom_tool.created_by,
modified_by=custom_tool.modified_by,
defaults={
"name": custom_tool.tool_name,
"tool_property": properties.to_dict(),
Expand All @@ -190,7 +189,7 @@ def update_or_create_psr_tool(
logger.info(f"PSR {obj.prompt_registry_id} was created")
else:
logger.info(f"PSR {obj.prompt_registry_id} was updated")

obj.modified_by = custom_tool.modified_by
obj.shared_to_org = shared_with_org
if not shared_with_org:
obj.shared_users.clear()
Expand Down
2 changes: 1 addition & 1 deletion unstract/connectors/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies = [
"s3fs[boto3]==2023.6.0", # For Minio
"PyDrive2[fsspec]==1.15.4", # For GDrive
"oauth2client==4.1.3", # For GDrive
"dropboxdrivefs==1.3.1", # For Dropbox
"dropboxdrivefs==1.4.1", # For Dropbox
"boxfs==0.2.1", # For Box
"gcsfs==2023.6.0", # For GoogleCloudStorage
"adlfs==2023.8.0", # For AzureCloudStorage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def test_credentials(self) -> bool:
try:
self.get_engine()
except Exception as e:
raise ConnectorError(str(e)) from e
raise ConnectorError(f"Error while connecting to DB: {str(e)}") from e
return True

def execute(self, query: str) -> Any:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ def get_fsspec_fs(self) -> AzureBlobFileSystem:
def test_credentials(self) -> bool:
"""To test credentials for Azure Cloud Storage."""
try:
self.get_fsspec_fs().ls(f"{self.bucket}")
is_dir = bool(self.get_fsspec_fs().isdir(self.bucket))
if not is_dir:
raise RuntimeError(f"'{self.bucket}' is not a valid bucket.")
except Exception as e:
raise ConnectorError(str(e))
raise ConnectorError(
f"Error from Azure Cloud Storage while testing connection: {str(e)}"
) from e
return True
13 changes: 10 additions & 3 deletions unstract/connectors/src/unstract/connectors/filesystems/box/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self, settings: dict[str, Any]):
settings_dict = json.loads(settings["box_app_settings"])
if not isinstance(settings_dict, dict):
raise ConnectorError(
"Box app settings is expected to be a valid JSON",
"Box app settings should be a valid JSON.",
treat_as_user_message=True,
)
except JSONDecodeError as e:
Expand Down Expand Up @@ -112,8 +112,15 @@ def get_fsspec_fs(self) -> BoxFileSystem:

def test_credentials(self) -> bool:
"""To test credentials for the Box connector."""
is_dir = False
try:
self.get_fsspec_fs().isdir("/")
is_dir = bool(self.get_fsspec_fs().isdir("/"))
except Exception as e:
raise ConnectorError(str(e))
raise ConnectorError(
f"Error from Box while testing connection: {str(e)}"
) from e
if not is_dir:
raise ConnectorError(
"Unable to connect to Box, please check the connection settings."
)
return True
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ def get_fsspec_fs(self) -> GCSFileSystem:
def test_credentials(self) -> bool:
"""To test credentials for Google Cloud Storage."""
try:
is_dir = bool(self.get_fsspec_fs().isdir(f"{self.bucket}"))
return is_dir
is_dir = bool(self.get_fsspec_fs().isdir(self.bucket))
if not is_dir:
raise RuntimeError(f"'{self.bucket}' is not a valid bucket.")
except Exception as e:
raise ConnectorError(str(e))
raise ConnectorError(
f"Error from Google Cloud Storage while testing connection: {str(e)}"
) from e
return True
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,18 @@ def get_fsspec_fs(self) -> GDriveFileSystem:

def test_credentials(self) -> bool:
"""To test credentials for Google Drive."""
is_dir = False
try:
self.get_fsspec_fs().isdir("root")
is_dir = bool(self.get_fsspec_fs().isdir("root"))
except Exception as e:
raise ConnectorError(str(e))
raise ConnectorError(
f"Error from Google Drive while testing connection: {str(e)}"
) from e
if not is_dir:
raise ConnectorError(
"Unable to connect to Google Drive, "
"please check the connection settings."
)
return True

@staticmethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,16 @@ def get_fsspec_fs(self) -> HTTPFileSystem:

def test_credentials(self) -> bool:
"""To test credentials for HTTP(S)."""
is_dir = False
try:
self.get_fsspec_fs().isdir("/")
is_dir = bool(self.get_fsspec_fs().isdir("/"))
except Exception as e:
raise ConnectorError(str(e))
raise ConnectorError(
f"Error while connecting to HTTP server: {str(e)}"
) from e
if not is_dir:
raise ConnectorError(
"Unable to connect to HTTP server, "
"please check the connection settings."
)
return True
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from fsspec.implementations.local import LocalFileSystem

from unstract.connectors.exceptions import ConnectorError
from unstract.connectors.filesystems.unstract_file_system import UnstractFileSystem

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -60,9 +61,16 @@ def get_fsspec_fs(self) -> Any:

def test_credentials(self, *args, **kwargs) -> bool: # type:ignore
"""To test credentials for LocalStorage."""
is_dir = False
try:
self.get_fsspec_fs().isdir("/")
is_dir = bool(self.get_fsspec_fs().isdir("/"))
except Exception as e:
logger.error(f"Test creds failed: {e}")
return False
raise ConnectorError(
f"Error while connecting to local storage: {str(e)}"
) from e
if not is_dir:
raise ConnectorError(
"Unable to connect to local storage, "
"please check the connection settings."
)
return True
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from unstract.connectors.exceptions import ConnectorError

S3FS_EXC_TO_UNSTRACT_EXC = {
"The AWS Access Key Id you provided does not exist in our records": (
"Invalid Key (Access Key ID) provided, please provide a valid one."
),
"The request signature we calculated does not match the signature you provided": (
"Invalid Secret (Secret Access Key) provided, please provide a valid one."
),
"[Errno 22] S3 API Requests must be made to API port": ( # Minio only
"Request made to invalid port, please check the port of the endpoint URL."
),
}


def handle_s3fs_exception(e: Exception) -> ConnectorError:
original_exc = str(e)
user_msg = "Error from S3 / MinIO while testing connection: "
exc_to_append = ""
for s3fs_exc, user_friendly_msg in S3FS_EXC_TO_UNSTRACT_EXC.items():
if s3fs_exc in original_exc:
exc_to_append = user_friendly_msg
break

user_msg += exc_to_append if exc_to_append else str(e)
return ConnectorError(message=user_msg)
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

from s3fs.core import S3FileSystem

from unstract.connectors.exceptions import ConnectorError
from unstract.connectors.filesystems.unstract_file_system import UnstractFileSystem

from .exceptions import handle_s3fs_exception

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -37,11 +38,11 @@ def get_id() -> str:

@staticmethod
def get_name() -> str:
return "MinioFS/S3"
return "S3/Minio"

@staticmethod
def get_description() -> str:
return "All MinioFS compatible, including AWS S3"
return "Connect to AWS S3 and other compatible storage such as Minio."

@staticmethod
def get_icon() -> str:
Expand Down Expand Up @@ -76,7 +77,9 @@ def get_fsspec_fs(self) -> S3FileSystem:
def test_credentials(self) -> bool:
"""To test credentials for Minio."""
try:
self.get_fsspec_fs().isdir(f"{self.bucket}")
is_dir = bool(self.get_fsspec_fs().isdir(self.bucket))
if not is_dir:
raise RuntimeError(f"'{self.bucket}' is not a valid bucket.")
except Exception as e:
raise ConnectorError(str(e))
raise handle_s3fs_exception(e) from e
return True
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@


def handle_dropbox_exception(e: DropboxException) -> ConnectorError:
user_msg = ""
user_msg = "Error from Dropbox while testing connection: "
if isinstance(e, ExcAuthError):
if isinstance(e.error, AuthError):
if e.error.is_expired_access_token():
user_msg = "Expired access token"
user_msg += (
"Expired access token, please regenerate it "
"through the Dropbox console."
)
elif e.error.is_invalid_access_token():
user_msg = "Invalid access token"
user_msg += (
"Invalid access token, please enter a valid token "
"from the Dropbox console."
)
else:
user_msg = e.error._tag
user_msg += e.error._tag
elif isinstance(e, ApiError):
if e.user_message_text is not None:
user_msg = e.user_message_text
user_msg += e.user_message_text
return ConnectorError(message=user_msg, treat_as_user_message=True)
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,9 @@ def test_credentials(self) -> bool:
# self.get_fsspec_fs().connect()
self.get_fsspec_fs().ls("")
except DropboxException as e:
logger.error(f"Test creds failed: {e}")
raise handle_dropbox_exception(e)
raise handle_dropbox_exception(e) from e
except Exception as e:
logger.error(f"Test creds failed: {e}")
raise ConnectorError(str(e))
raise ConnectorError(f"Error while connecting to Dropbox: {str(e)}") from e
return True

@staticmethod
Expand Down

0 comments on commit 0d40e67

Please sign in to comment.