diff --git a/docs/data-sources/builtin-toolsets/servicenow.md b/docs/data-sources/builtin-toolsets/servicenow.md index 2e437883b..e64d30e86 100644 --- a/docs/data-sources/builtin-toolsets/servicenow.md +++ b/docs/data-sources/builtin-toolsets/servicenow.md @@ -75,6 +75,10 @@ You should receive a JSON response. If you get an authentication error, check yo config: api_key: # e.g. now_1234567890abcdef api_url: # e.g. https://dev12345.service-now.com + + # Optional + api_key_header: x-sn-apikey # HTTP header name for the API key (default: x-sn-apikey) + health_check_table: sys_user # Table used to verify connectivity on startup (default: sys_user) ``` --8<-- "snippets/toolset_refresh_warning.md" @@ -95,8 +99,19 @@ You should receive a JSON response. If you get an authentication error, check yo config: api_key: # e.g. now_1234567890abcdef api_url: # e.g. https://dev12345.service-now.com + + # Optional + api_key_header: x-sn-apikey # HTTP header name for the API key (default: x-sn-apikey) + health_check_table: sys_user # Table used to verify connectivity on startup (default: sys_user) ``` +### Optional Fields + +| Option | Default | Description | +|--------|---------|-------------| +| `api_key_header` | `x-sn-apikey` | HTTP header name used to pass the API key. Change this if your ServiceNow instance uses a custom authentication header. | +| `health_check_table` | `sys_user` | Table queried on startup to verify connectivity and permissions. Change this if your API key doesn't have access to the default table. | + ## Capabilities | Tool Name | Description | diff --git a/holmes/plugins/toolsets/servicenow_tables/servicenow_tables.py b/holmes/plugins/toolsets/servicenow_tables/servicenow_tables.py index 0757258da..b86ca1dea 100644 --- a/holmes/plugins/toolsets/servicenow_tables/servicenow_tables.py +++ b/holmes/plugins/toolsets/servicenow_tables/servicenow_tables.py @@ -49,6 +49,12 @@ class ServiceNowTablesConfig(ToolsetConfig): description="HTTP header name to use for passing the API key", examples=["x-sn-apikey"], ) + health_check_table: str = Field( + default="sys_user", + title="Health check table", + description="Table queried on startup to verify connectivity and permissions. Change this if your API key doesn't have access to the default table.", + examples=["sys_user", "incident", "sys_db_object"], + ) class ServiceNowTablesToolset(Toolset): @@ -77,43 +83,42 @@ def prerequisites_callable(self, config: dict[str, Any]) -> Tuple[bool, str]: # Validate the config using Pydantic - this will raise if required fields are missing self.config = ServiceNowTablesConfig(**config) - # Perform health check - return self._perform_health_check() + return self._perform_health_check(table_name=self.config.health_check_table) except Exception as e: return False, f"Failed to validate ServiceNow configuration: {str(e)}" - def _perform_health_check(self) -> Tuple[bool, str]: + def _perform_health_check(self, table_name) -> Tuple[bool, str]: """Perform a health check by making a minimal API call.""" try: # Query sys_db_object table with minimal data - data, headers = self._make_api_request( - endpoint="api/now/v2/table/sys_db_object", - query_params={"sysparm_limit": 1, "sysparm_fields": "sys_id"}, + _, _ = self._make_api_request( + endpoint=f"api/now/v2/table/{table_name}", + query_params={"sysparm_limit": 1}, timeout=10, ) - return True, "ServiceNow configuration is valid and API is accessible." + return True, f"ServiceNow configuration is valid and API is accessible. (checked table: {table_name})" except requests.exceptions.HTTPError as e: if e.response.status_code == 401: return ( False, - "ServiceNow authentication failed. Please check your API key.", + f"ServiceNow authentication failed. Please check your API key. Full error: {e.response.status_code} - {e.response.text}", ) elif e.response.status_code == 403: return ( False, - "ServiceNow access denied. Please ensure your user has Table API access.", + f"ServiceNow access denied. Please ensure your user has Table API access. Full error: {e.response.status_code} - {e.response.text}", ) else: return ( False, f"ServiceNow API returned error: {e.response.status_code} - {e.response.text}", ) - except requests.exceptions.ConnectionError: + except requests.exceptions.ConnectionError as e: return ( False, - f"Failed to connect to ServiceNow instance at {self.config.api_url if self.config else 'unknown'}", + f"Failed to connect to ServiceNow instance at {self.config.api_url if self.config else 'unknown'}. Full error: {str(e)}", ) except requests.exceptions.Timeout: return False, "ServiceNow health check timed out" diff --git a/tests/integration/test_kubernetes_transformer_execution.py b/tests/integration/test_kubernetes_transformer_execution.py index 985e37bf1..0132f75d6 100644 --- a/tests/integration/test_kubernetes_transformer_execution.py +++ b/tests/integration/test_kubernetes_transformer_execution.py @@ -514,12 +514,13 @@ class TestTransformerPerformanceMetrics: def setup_method(self): """Set up test fixtures.""" - # Track whether llm_summarize was originally registered for restoration - self.llm_summarize_was_registered = registry.is_registered("llm_summarize") - - # Clean up existing registrations + # Save original transformer registration if it exists + self.original_llm_summarize = None if registry.is_registered("llm_summarize"): + self.original_llm_summarize = registry._transformers["llm_summarize"] registry.unregister("llm_summarize") + + # Clean up any mock registrations if registry.is_registered("MockSummarizeTransformer"): registry.unregister("MockSummarizeTransformer") @@ -534,8 +535,9 @@ def teardown_method(self): if registry.is_registered("MockSummarizeTransformer"): registry.unregister("MockSummarizeTransformer") - # Note: Original transformer restoration is not possible without accessing private registry attributes. - # Test isolation is maintained by cleaning up our mock registrations. + # Restore original transformer if it existed + if self.original_llm_summarize is not None: + registry.register(self.original_llm_summarize) def test_transformer_performance_logging(self): """Test that transformer execution metrics are logged."""