diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/ClusterMetricsInput.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/ClusterMetricsInput.kt index 7016da82..2dbf4abe 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/ClusterMetricsInput.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/ClusterMetricsInput.kt @@ -148,9 +148,10 @@ data class ClusterMetricsInput( } if (apiType.isNotEmpty() && path.isNotEmpty()) { + val formattedPath = path.trim('/') val derivedType = ClusterMetricType.values() .filter { it != ClusterMetricType.BLANK } - .find { path.startsWith(it.prependPath) || path.startsWith(it.defaultPath) } + .find { formattedPath.startsWith(it.prependPath.trim('/')) || formattedPath.startsWith(it.defaultPath.trim('/')) } require(derivedType != null && derivedType.name == apiType) { "The provided api_type [$apiType] does not match the path [$path]." diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/ClusterMetricsInputTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/ClusterMetricsInputTests.kt index 6987ed19..23753c29 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/model/ClusterMetricsInputTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/ClusterMetricsInputTests.kt @@ -630,4 +630,84 @@ class ClusterMetricsInputTests { val input = ClusterMetricsInput.parseInner(xcp) assertEquals(ClusterMetricsInput.ClusterMetricType.CAT_INDICES, input.clusterMetricType) } + + @Test + fun `test parseInner accepts matching api_type and path without leading slash`() { + val inputJson = """ + {"uri":{"api_type":"CAT_INDICES","path":"_cat/indices","path_params":"","url":"","clusters":[]}} + """.trimIndent() + val xcp = org.opensearch.common.xcontent.json.JsonXContent.jsonXContent + .createParser( + org.opensearch.core.xcontent.NamedXContentRegistry.EMPTY, + org.opensearch.core.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION, + inputJson + ) + xcp.nextToken() + xcp.nextToken() + xcp.nextToken() + + val input = ClusterMetricsInput.parseInner(xcp) + assertEquals(ClusterMetricsInput.ClusterMetricType.CAT_INDICES, input.clusterMetricType) + } + + @Test + fun `test parseInner rejects mismatched api_type and path without leading slash`() { + val inputJson = """ + {"uri":{"api_type":"CLUSTER_STATS","path":"_cat/indices","path_params":"","url":"","clusters":[]}} + """.trimIndent() + val xcp = org.opensearch.common.xcontent.json.JsonXContent.jsonXContent + .createParser( + org.opensearch.core.xcontent.NamedXContentRegistry.EMPTY, + org.opensearch.core.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION, + inputJson + ) + xcp.nextToken() + xcp.nextToken() + xcp.nextToken() + + assertFailsWith("The provided api_type") { + ClusterMetricsInput.parseInner(xcp) + } + } + + @Test + fun `test parseInner accepts matching api_type with path containing trailing slash`() { + val inputJson = """ + {"uri":{"api_type":"CLUSTER_HEALTH","path":"/_cluster/health/","path_params":"","url":"","clusters":[]}} + """.trimIndent() + val xcp = org.opensearch.common.xcontent.json.JsonXContent.jsonXContent + .createParser( + org.opensearch.core.xcontent.NamedXContentRegistry.EMPTY, + org.opensearch.core.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION, + inputJson + ) + xcp.nextToken() + xcp.nextToken() + xcp.nextToken() + + val input = ClusterMetricsInput.parseInner(xcp) + assertEquals(ClusterMetricsInput.ClusterMetricType.CLUSTER_HEALTH, input.clusterMetricType) + } + + @Test + fun `test parseInner rejects slash-only path with api_type`() { + // path "/" enters validation since path.isNotEmpty(), + // but trimmed path doesn't match any ClusterMetricType + val inputJson = """ + {"uri":{"api_type":"CAT_INDICES","path":"/","path_params":"","url":"http://localhost:9200/_cluster/health","clusters":[]}} + """.trimIndent() + val xcp = org.opensearch.common.xcontent.json.JsonXContent.jsonXContent + .createParser( + org.opensearch.core.xcontent.NamedXContentRegistry.EMPTY, + org.opensearch.core.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION, + inputJson + ) + xcp.nextToken() + xcp.nextToken() + xcp.nextToken() + + assertFailsWith("The provided api_type") { + ClusterMetricsInput.parseInner(xcp) + } + } }