From f89689b3bee84420a9e30a3e58a2ee11f83f9053 Mon Sep 17 00:00:00 2001 From: jayeshsadhwani99 Date: Thu, 13 Nov 2025 16:29:21 +0530 Subject: [PATCH 1/6] hanldes fixes for dd api processor --- .../source_api_processors/datadog_api_processor.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py b/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py index af8432f..0a07e79 100644 --- a/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py +++ b/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py @@ -435,13 +435,25 @@ def test_connection(self): try: configuration = self.get_connection() with ApiClient(configuration) as api_client: + # Validate API Key api_instance = AuthenticationApi(api_client) response: AuthenticationValidationResponse = api_instance.validate() if not response.get('valid', False): raise Exception("Datadog API connection is not valid. Check API Key") + + # Validate Application Key by making a call that requires it + monitors_api_instance = MonitorsApi(api_client) + monitors_api_instance.list_monitors(page_size=1) + return True except ApiException as e: - logger.error("Exception when calling AuthenticationApi->validate: %s\n" % e) + logger.error("Exception when calling Datadog API: %s\n" % e) + if "AuthenticationApi" in str(e) or "validate" in str(e): + raise Exception("Datadog API Key is not valid. Check API Key") + else: + raise Exception("Datadog Application Key is not valid. Check Application Key") + except Exception as e: + logger.error("Exception when testing Datadog connection: %s\n" % e) raise e def fetch_metric_timeseries(self, tr: TimeRange, specific_metric, interval=300000): From 7e06cff052e96cb3fdfaebda47c31597e93f9fbe Mon Sep 17 00:00:00 2001 From: jayeshsadhwani99 Date: Thu, 13 Nov 2025 16:40:52 +0530 Subject: [PATCH 2/6] checks for api and app key --- .../datadog_api_processor.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py b/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py index 0a07e79..c53974e 100644 --- a/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py +++ b/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py @@ -435,22 +435,36 @@ def test_connection(self): try: configuration = self.get_connection() with ApiClient(configuration) as api_client: - # Validate API Key + # Validate API Key (this endpoint only requires API key) api_instance = AuthenticationApi(api_client) response: AuthenticationValidationResponse = api_instance.validate() if not response.get('valid', False): - raise Exception("Datadog API connection is not valid. Check API Key") + raise Exception("Datadog API Key is not valid. Check API Key") - # Validate Application Key by making a call that requires it + # Validate Application Key by making a call that requires both keys + # Since API key is already validated, if this fails it's likely the Application Key monitors_api_instance = MonitorsApi(api_client) monitors_api_instance.list_monitors(page_size=1) return True except ApiException as e: logger.error("Exception when calling Datadog API: %s\n" % e) - if "AuthenticationApi" in str(e) or "validate" in str(e): + # Check if this is from the validate() call (API key validation) + if hasattr(e, 'status') and e.status == 403: + # 403 Forbidden typically means Application Key is invalid + # (since we already validated API key above) + raise Exception("Datadog Application Key is not valid. Check Application Key") + elif hasattr(e, 'status') and e.status == 401: + # 401 Unauthorized could be either, but if we got past validate(), it's likely App Key + error_msg = str(e).lower() + if "application" in error_msg or "app" in error_msg: + raise Exception("Datadog Application Key is not valid. Check Application Key") + else: + raise Exception("Datadog API Key is not valid. Check API Key") + elif "AuthenticationApi" in str(e) or "validate" in str(e): raise Exception("Datadog API Key is not valid. Check API Key") else: + # Default: assume Application Key issue since API key was already validated raise Exception("Datadog Application Key is not valid. Check Application Key") except Exception as e: logger.error("Exception when testing Datadog connection: %s\n" % e) From 926781f9b2373c009c7d979670c6471c7e6bc33c Mon Sep 17 00:00:00 2001 From: jayeshsadhwani99 Date: Thu, 13 Nov 2025 16:53:11 +0530 Subject: [PATCH 3/6] adds error handling for application key --- .../datadog_api_processor.py | 88 +++++++++++++------ 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py b/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py index c53974e..c2b2105 100644 --- a/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py +++ b/drdroid_debug_toolkit/core/integrations/source_api_processors/datadog_api_processor.py @@ -436,39 +436,75 @@ def test_connection(self): configuration = self.get_connection() with ApiClient(configuration) as api_client: # Validate API Key (this endpoint only requires API key) - api_instance = AuthenticationApi(api_client) - response: AuthenticationValidationResponse = api_instance.validate() - if not response.get('valid', False): + try: + api_instance = AuthenticationApi(api_client) + response: AuthenticationValidationResponse = api_instance.validate() + if not response.get('valid', False): + raise Exception("Datadog API Key is not valid. Check API Key") + except ApiException as e: + logger.error("Exception when validating API Key: %s\n" % e) raise Exception("Datadog API Key is not valid. Check API Key") # Validate Application Key by making a call that requires both keys - # Since API key is already validated, if this fails it's likely the Application Key - monitors_api_instance = MonitorsApi(api_client) - monitors_api_instance.list_monitors(page_size=1) + # Since API key is already validated, check error body for specific Application Key errors + try: + monitors_api_instance = MonitorsApi(api_client) + monitors_api_instance.list_monitors(page_size=1) + except ApiException as e: + logger.error("Exception when validating Application Key: %s\n" % e) + # Check the error body for specific messages about Application Key + error_body = e.body if hasattr(e, 'body') else None + error_str = str(e).lower() + + # Check if error explicitly mentions Application Key + app_key_indicators = [ + "application key", + "app key", + "appkeyauth", + "dd-application-key", + "invalid application key", + "missing application key" + ] + + # Check if error explicitly mentions API Key + api_key_indicators = [ + "api key", + "apikeyauth", + "dd-api-key", + "invalid api key", + "missing api key" + ] + + # Check error body (could be dict or string) + body_str = "" + if isinstance(error_body, dict): + body_str = str(error_body).lower() + elif isinstance(error_body, str): + body_str = error_body.lower() + + # Check if error message specifically mentions Application Key + if any(indicator in error_str or indicator in body_str for indicator in app_key_indicators): + raise Exception("Datadog Application Key is not valid. Check Application Key") + # Check if error message specifically mentions API Key + elif any(indicator in error_str or indicator in body_str for indicator in api_key_indicators): + raise Exception("Datadog API Key is not valid. Check API Key") + # If status is 403 and we got past API key validation, it's likely Application Key + elif hasattr(e, 'status') and e.status == 403: + raise Exception("Datadog Application Key is not valid. Check Application Key") + # Otherwise, provide a generic error message + else: + error_details = f"Status: {e.status if hasattr(e, 'status') else 'Unknown'}" + if error_body: + error_details += f", Error: {error_body}" + raise Exception(f"Datadog connection failed. {error_details}. Please check both API Key and Application Key") return True - except ApiException as e: - logger.error("Exception when calling Datadog API: %s\n" % e) - # Check if this is from the validate() call (API key validation) - if hasattr(e, 'status') and e.status == 403: - # 403 Forbidden typically means Application Key is invalid - # (since we already validated API key above) - raise Exception("Datadog Application Key is not valid. Check Application Key") - elif hasattr(e, 'status') and e.status == 401: - # 401 Unauthorized could be either, but if we got past validate(), it's likely App Key - error_msg = str(e).lower() - if "application" in error_msg or "app" in error_msg: - raise Exception("Datadog Application Key is not valid. Check Application Key") - else: - raise Exception("Datadog API Key is not valid. Check API Key") - elif "AuthenticationApi" in str(e) or "validate" in str(e): - raise Exception("Datadog API Key is not valid. Check API Key") - else: - # Default: assume Application Key issue since API key was already validated - raise Exception("Datadog Application Key is not valid. Check Application Key") except Exception as e: + # Re-raise if it's already our custom exception + if "Datadog" in str(e) and ("API Key" in str(e) or "Application Key" in str(e) or "connection failed" in str(e)): + raise e logger.error("Exception when testing Datadog connection: %s\n" % e) - raise e + raise Exception(f"Datadog connection failed: {str(e)}. Please check both API Key and Application Key") def fetch_metric_timeseries(self, tr: TimeRange, specific_metric, interval=300000): metric_queries = specific_metric.get('queries', None) From 111ece3141dce0fed164f8acd7e8a6d23903865f Mon Sep 17 00:00:00 2001 From: jayeshsadhwani99 Date: Thu, 13 Nov 2025 19:33:16 +0530 Subject: [PATCH 4/6] use user defined region --- .../source_managers/cloudwatch_source_manager.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py b/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py index f294884..01e50d9 100644 --- a/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py +++ b/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py @@ -380,9 +380,21 @@ def test_connector_processor(self, connector: ConnectorProto, **kwargs): Returns: Tuple[bool, str or dict]: Success status and either a formatted message or task permission mapping. """ - # Define CloudWatch specific defaults if not provided in kwargs + # Extract region from connector if not provided in kwargs if 'region' not in kwargs: - kwargs['region'] = 'us-west-2' # Default region for CloudWatch checks + # Extract region from connector keys + region = None + for conn_key in connector.keys: + if conn_key.key_type == SourceKeyType.AWS_REGION: + region = conn_key.key.value + break + + if region: + kwargs['region'] = region + else: + # Fallback to default if region not found in connector + kwargs['region'] = 'us-west-2' + logger.warning("Region not found in connector keys, using default: us-west-2") # Delegate the detailed permission checking to the base class implementation return self.test_connector_permissions(connector, **kwargs) From 0d279ffdafa714b6422cc7ad454093f70a58c877 Mon Sep 17 00:00:00 2001 From: jayeshsadhwani99 Date: Thu, 13 Nov 2025 20:19:46 +0530 Subject: [PATCH 5/6] hanldes cloudwatch region --- .../cloudwatch_source_manager.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py b/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py index 01e50d9..3f85c84 100644 --- a/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py +++ b/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py @@ -365,7 +365,7 @@ def __init__(self): def get_connector_processor(self, cloudwatch_connector, **kwargs): generated_credentials = generate_credentials_dict(cloudwatch_connector.type, cloudwatch_connector.keys) generated_credentials['client_type'] = kwargs.get('client_type', 'cloudwatch') - if 'region' in kwargs: + if 'region' in kwargs and kwargs.get('region'): generated_credentials['region'] = kwargs.get('region') return AWSBoto3ApiProcessor(**generated_credentials) @@ -380,22 +380,6 @@ def test_connector_processor(self, connector: ConnectorProto, **kwargs): Returns: Tuple[bool, str or dict]: Success status and either a formatted message or task permission mapping. """ - # Extract region from connector if not provided in kwargs - if 'region' not in kwargs: - # Extract region from connector keys - region = None - for conn_key in connector.keys: - if conn_key.key_type == SourceKeyType.AWS_REGION: - region = conn_key.key.value - break - - if region: - kwargs['region'] = region - else: - # Fallback to default if region not found in connector - kwargs['region'] = 'us-west-2' - logger.warning("Region not found in connector keys, using default: us-west-2") - # Delegate the detailed permission checking to the base class implementation return self.test_connector_permissions(connector, **kwargs) From 128b74a051a3dc5e7af2c9c1f7ac4fad4eb8a271 Mon Sep 17 00:00:00 2001 From: jayeshsadhwani99 Date: Thu, 13 Nov 2025 20:26:12 +0530 Subject: [PATCH 6/6] revert cw changes --- .../source_managers/cloudwatch_source_manager.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py b/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py index 3f85c84..f294884 100644 --- a/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py +++ b/drdroid_debug_toolkit/core/integrations/source_managers/cloudwatch_source_manager.py @@ -365,7 +365,7 @@ def __init__(self): def get_connector_processor(self, cloudwatch_connector, **kwargs): generated_credentials = generate_credentials_dict(cloudwatch_connector.type, cloudwatch_connector.keys) generated_credentials['client_type'] = kwargs.get('client_type', 'cloudwatch') - if 'region' in kwargs and kwargs.get('region'): + if 'region' in kwargs: generated_credentials['region'] = kwargs.get('region') return AWSBoto3ApiProcessor(**generated_credentials) @@ -380,6 +380,10 @@ def test_connector_processor(self, connector: ConnectorProto, **kwargs): Returns: Tuple[bool, str or dict]: Success status and either a formatted message or task permission mapping. """ + # Define CloudWatch specific defaults if not provided in kwargs + if 'region' not in kwargs: + kwargs['region'] = 'us-west-2' # Default region for CloudWatch checks + # Delegate the detailed permission checking to the base class implementation return self.test_connector_permissions(connector, **kwargs)