diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-automation/gravitee-apim-rest-api-automation-rest/src/test/java/io/gravitee/apim/rest/api/automation/spring/ResourceContextConfiguration.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-automation/gravitee-apim-rest-api-automation-rest/src/test/java/io/gravitee/apim/rest/api/automation/spring/ResourceContextConfiguration.java
index 1d25941a7d9..7418a6516ba 100644
--- a/gravitee-apim-rest-api/gravitee-apim-rest-api-automation/gravitee-apim-rest-api-automation-rest/src/test/java/io/gravitee/apim/rest/api/automation/spring/ResourceContextConfiguration.java
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-automation/gravitee-apim-rest-api-automation-rest/src/test/java/io/gravitee/apim/rest/api/automation/spring/ResourceContextConfiguration.java
@@ -37,6 +37,11 @@
import inmemory.SharedPolicyGroupCrudServiceInMemory;
import inmemory.SharedPolicyGroupHistoryCrudServiceInMemory;
import inmemory.spring.InMemoryConfiguration;
+import io.gravitee.apim.core.analytics.domain_service.engine.definition.AnalyticsDefinition;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiMetricsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiSpecsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFacetsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFiltersUseCase;
import io.gravitee.apim.core.api.domain_service.ApiExportDomainService;
import io.gravitee.apim.core.api.domain_service.ApiImportDomainService;
import io.gravitee.apim.core.api.domain_service.ApiMetadataDecoderDomainService;
@@ -129,6 +134,7 @@
import io.gravitee.apim.core.subscription.use_case.RejectSubscriptionUseCase;
import io.gravitee.apim.infra.adapter.SubscriptionAdapter;
import io.gravitee.apim.infra.adapter.SubscriptionAdapterImpl;
+import io.gravitee.apim.infra.domain_service.analytics.AnalyticsDefinitionDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.application.ValidateApplicationSettingsDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.documentation.ValidatePageSourceDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.group.ValidateGroupCRDDomainServiceImpl;
@@ -874,4 +880,29 @@ public CockpitPromotionServiceProvider cockpitPromotionServiceProvider() {
public CreatePromotionUseCase createPromotionUseCase() {
return mock(CreatePromotionUseCase.class);
}
+
+ @Bean
+ public AnalyticsDefinition analyticsDefinitionDomainService() {
+ return new AnalyticsDefinitionDomainServiceImpl();
+ }
+
+ @Bean
+ public GetApiSpecsUseCase getApiSpecsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetApiSpecsUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetApiMetricsUseCase getApiMetricsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetApiMetricsUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetMetricFiltersUseCase getMetricFiltersUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetMetricFiltersUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetMetricFacetsUseCase getMetricFacetsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetMetricFacetsUseCase(analyticsDefinition);
+ }
}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/pom.xml b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/pom.xml
index 479bf30a94f..1424c7b4a1c 100644
--- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/pom.xml
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/pom.xml
@@ -293,7 +293,18 @@
${project.basedir}/src/main/resources/openapi/openapi-installation-deprecated.yaml
+
+ model-analytics
+
+ generate
+
+
+ ${project.basedir}/src/main/resources/openapi/openapi-analytics.yaml
+ ${openapi.modelPackage}.analytics.engine
+
+
+
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/GraviteeManagementV2Application.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/GraviteeManagementV2Application.java
index 6524b3209f1..47ced5227b5 100644
--- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/GraviteeManagementV2Application.java
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/GraviteeManagementV2Application.java
@@ -33,6 +33,7 @@
import io.gravitee.rest.api.management.v2.rest.provider.ObjectMapperResolver;
import io.gravitee.rest.api.management.v2.rest.provider.YamlWriter;
import io.gravitee.rest.api.management.v2.rest.resource.OpenAPIResource;
+import io.gravitee.rest.api.management.v2.rest.resource.analytics.definition.AnalyticsDefinitionResource;
import io.gravitee.rest.api.management.v2.rest.resource.api.ApisResource;
import io.gravitee.rest.api.management.v2.rest.resource.application.ApplicationsResource;
import io.gravitee.rest.api.management.v2.rest.resource.asyncjob.AsyncJobsResource;
@@ -73,6 +74,7 @@ public GraviteeManagementV2Application() {
register(EnvironmentsResource.class);
register(ApisResource.class);
register(ApplicationsResource.class);
+ register(AnalyticsDefinitionResource.class);
// Resources deprecated at root level
register(EndpointsResource.class);
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/mapper/AnalyticsDefinitionMapper.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/mapper/AnalyticsDefinitionMapper.java
new file mode 100644
index 00000000000..702d86420d7
--- /dev/null
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/mapper/AnalyticsDefinitionMapper.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.rest.api.management.v2.rest.mapper;
+
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.ApiSpec;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.ApiSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.FacetSpec;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.FacetSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.FilterSpec;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.FilterSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.MetricSpec;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.MetricSpecsResponse;
+import java.util.List;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface AnalyticsDefinitionMapper {
+ AnalyticsDefinitionMapper INSTANCE = Mappers.getMapper(AnalyticsDefinitionMapper.class);
+
+ ApiSpec mapApiSpec(io.gravitee.apim.core.analytics.model.engine.definition.ApiSpec apiSpec);
+
+ List mapApiSpecs(List apiSpecs);
+
+ default ApiSpecsResponse toApiSpecsResponse(List apiSpecs) {
+ return new ApiSpecsResponse().data(mapApiSpecs(apiSpecs));
+ }
+
+ MetricSpec mapMetricSpec(io.gravitee.apim.core.analytics.model.engine.definition.MetricSpec metricSpec);
+
+ List mapMetricSpecs(List metricSpecs);
+
+ default MetricSpecsResponse toMetricSpecsResponse(
+ List metricSpecs
+ ) {
+ return new MetricSpecsResponse().data(mapMetricSpecs(metricSpecs));
+ }
+
+ FacetSpec mapFacetSpec(io.gravitee.apim.core.analytics.model.engine.definition.FacetSpec facetSpec);
+
+ List mapFacetSpecs(List facetSpecs);
+
+ default FacetSpecsResponse toFacetSpecsResponse(List facetSpecs) {
+ return new FacetSpecsResponse().data(mapFacetSpecs(facetSpecs));
+ }
+
+ FilterSpec mapFilterSpec(io.gravitee.apim.core.analytics.model.engine.definition.FilterSpec filterSpec);
+
+ List mapFilterSpecs(List filterSpecs);
+
+ default FilterSpecsResponse toFilterSpecsResponse(
+ List filterSpecs
+ ) {
+ return new FilterSpecsResponse().data(mapFilterSpecs(filterSpecs));
+ }
+}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/analytics/definition/AnalyticsDefinitionResource.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/analytics/definition/AnalyticsDefinitionResource.java
new file mode 100644
index 00000000000..e9112653ba8
--- /dev/null
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/analytics/definition/AnalyticsDefinitionResource.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.rest.api.management.v2.rest.resource.analytics.definition;
+
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiMetricsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiSpecsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFacetsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFiltersUseCase;
+import io.gravitee.rest.api.management.v2.rest.mapper.AnalyticsDefinitionMapper;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.ApiSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.FacetSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.FilterSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.MetricSpecsResponse;
+import io.gravitee.rest.api.model.permissions.RolePermission;
+import io.gravitee.rest.api.model.permissions.RolePermissionAction;
+import io.gravitee.rest.api.rest.annotation.Permission;
+import io.gravitee.rest.api.rest.annotation.Permissions;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+public class AnalyticsDefinitionResource {
+
+ @Inject
+ GetApiSpecsUseCase getApiSpecsUseCase;
+
+ @Inject
+ GetApiMetricsUseCase getApiMetricsUseCase;
+
+ @Inject
+ GetMetricFacetsUseCase getMetricFacetsUseCase;
+
+ @Inject
+ GetMetricFiltersUseCase getMetricFiltersUseCase;
+
+ @Path("/apis")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Permissions({ @Permission(value = RolePermission.API_ANALYTICS, acls = { RolePermissionAction.READ }) })
+ public ApiSpecsResponse getApiSpecs() {
+ return AnalyticsDefinitionMapper.INSTANCE.toApiSpecsResponse(getApiSpecsUseCase.execute().specs());
+ }
+
+ @Path("/apis/{apiName}/metrics")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Permissions({ @Permission(value = RolePermission.API_ANALYTICS, acls = { RolePermissionAction.READ }) })
+ public MetricSpecsResponse getApiMetrics(@PathParam("apiName") String apiName) {
+ return AnalyticsDefinitionMapper.INSTANCE.toMetricSpecsResponse(
+ getApiMetricsUseCase.execute(new GetApiMetricsUseCase.Input(apiName)).specs()
+ );
+ }
+
+ @Path("/metrics/{metricName}/facets")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Permissions({ @Permission(value = RolePermission.API_ANALYTICS, acls = { RolePermissionAction.READ }) })
+ public FacetSpecsResponse getMetricFacets(@PathParam("metricName") String metricName) {
+ return AnalyticsDefinitionMapper.INSTANCE.toFacetSpecsResponse(
+ getMetricFacetsUseCase.execute(new GetMetricFacetsUseCase.Input(metricName)).specs()
+ );
+ }
+
+ @Path("/metrics/{metricName}/filters")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Permissions({ @Permission(value = RolePermission.API_ANALYTICS, acls = { RolePermissionAction.READ }) })
+ public FilterSpecsResponse getMetricFilters(@PathParam("apiName") String apiName, @PathParam("metricName") String metricName) {
+ return AnalyticsDefinitionMapper.INSTANCE.toFilterSpecsResponse(
+ getMetricFiltersUseCase.execute(new GetMetricFiltersUseCase.Input(metricName)).specs()
+ );
+ }
+}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/environment/EnvironmentAnalyticsResource.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/environment/EnvironmentAnalyticsResource.java
index 9146644f9eb..5973c68bd61 100644
--- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/environment/EnvironmentAnalyticsResource.java
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/java/io/gravitee/rest/api/management/v2/rest/resource/environment/EnvironmentAnalyticsResource.java
@@ -32,7 +32,9 @@
import io.gravitee.rest.api.management.v2.rest.model.EnvironmentAnalyticsTopAppsByRequestCountResponse;
import io.gravitee.rest.api.management.v2.rest.model.EnvironmentAnalyticsTopFailedApisResponse;
import io.gravitee.rest.api.management.v2.rest.model.EnvironmentAnalyticsTopHitsApisResponse;
+import io.gravitee.rest.api.management.v2.rest.resource.analytics.definition.AnalyticsDefinitionResource;
import io.gravitee.rest.api.management.v2.rest.resource.environment.param.TimeRangeParam;
+import io.gravitee.rest.api.management.v2.rest.resource.plugin.PoliciesResource;
import io.gravitee.rest.api.model.permissions.RolePermission;
import io.gravitee.rest.api.model.permissions.RolePermissionAction;
import io.gravitee.rest.api.rest.annotation.Permission;
@@ -45,6 +47,8 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.container.ResourceContext;
+import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import java.time.Duration;
import java.time.Instant;
@@ -52,6 +56,9 @@
public class EnvironmentAnalyticsResource {
+ @Context
+ private ResourceContext resourceContext;
+
@Inject
SearchEnvironmentResponseStatusRangesUseCase searchEnvironmentResponseStatusRangesUseCase;
@@ -193,4 +200,9 @@ public EnvironmentAnalyticsTopFailedApisResponse getTopFailedApis(@BeanParam @Va
return EnvironmentAnalyticsMapper.INSTANCE.map(topFailedApis);
}
+
+ @Path("/definition")
+ public AnalyticsDefinitionResource getAnalyticsDefinitionApisResource() {
+ return resourceContext.getResource(AnalyticsDefinitionResource.class);
+ }
}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/resources/openapi/openapi-analytics.yaml b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/resources/openapi/openapi-analytics.yaml
index 780032ebc71..b8281fc44fb 100644
--- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/resources/openapi/openapi-analytics.yaml
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/main/resources/openapi/openapi-analytics.yaml
@@ -24,7 +24,8 @@ info:
tags:
- name: computation
description: Analytics computation operations for data processing and aggregation
-
+ - name: definition
+ description: Gravitee analytics definition containing metrics, facets and filters that can be used to query analytics data.
servers:
- url: "/management/v2"
description: APIM Management API v2 - Default base URL
@@ -36,6 +37,208 @@ servers:
default: DEFAULT
paths:
+ /environments/{envId}/analytics/definition/apis:
+ get:
+ operationId: queryApiSpecs
+ summary: Query available APIs for analytics
+ description: |
+ Returns a list of available APIs for analytics, that can be used to obtain available metrics each one of them.
+ tags:
+ - definition
+ parameters:
+ - $ref: "#/components/parameters/EnvId"
+ responses:
+ "200":
+ description: |
+ The response contains a list of available APIs for analytics, that can be used to obtain available metrics each one of them.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ApiSpecsResponse"
+ examples:
+ apis:
+ summary: API Specs Response
+ value:
+ data:
+ - name: "HTTP_PROXY"
+ label: "HTTP Proxy"
+ - name: "MESSAGE"
+ label: "Message"
+ - name: "KAFKA"
+ label: "Kafka"
+ - name: "LLM"
+ label: "LLM"
+ - name: "MCP"
+ label: "MCP"
+ "500":
+ description: Internal server error while getting available API specs.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+
+ /environments/{envId}/analytics/definition/apis/{apiName}/metrics:
+ get:
+ operationId: queryMetricSpecs
+ summary: Query available metrics for an API type
+ description: |
+ Returns a list of available metrics for a specific API type, including their supported measures, facets, and filters.
+ tags:
+ - definition
+ parameters:
+ - $ref: "#/components/parameters/EnvId"
+ - $ref: "#/components/parameters/ApiName"
+ responses:
+ "200":
+ description: |
+ The response contains a list of available metrics for the specified API type, with their configurations including measures, facets, and filters.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/MetricSpecsResponse"
+ examples:
+ http-proxy-metrics:
+ summary: HTTP Proxy API Metrics
+ value:
+ data:
+ - name: "HTTP_REQUESTS"
+ label: "HTTP Requests"
+ apis: ["HTTP_PROXY", "LLM"]
+ type: "COUNTER"
+ measures: ["COUNT", "RPS"]
+ facets: ["API", "APPLICATION", "GATEWAY"]
+ filters: ["API", "APPLICATION", "HTTP_STATUS"]
+ - name: "HTTP_GATEWAY_RESPONSE_TIME"
+ label: "Gateway response time"
+ apis: ["HTTP_PROXY", "LLM"]
+ type: "GAUGE"
+ unit: "MILLISECONDS"
+ measures: ["AVG", "MIN", "MAX", "P50", "P90", "P95", "P99"]
+ facets: ["API", "APPLICATION", "GATEWAY"]
+ filters: ["API", "APPLICATION", "HTTP_STATUS"]
+ "400":
+ description: Bad request - Invalid API name for metrics.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ "500":
+ description: Internal server error while getting metric specifications.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+
+ /environments/{envId}/analytics/definition/metrics/{metricName}/filters:
+ get:
+ operationId: queryFilterSpecsForMetric
+ summary: Query available filters for a specific metric
+ description: |
+ Returns a list of available filters that can be applied to a specific metric within an API type, including their operators and value constraints.
+ tags:
+ - definition
+ parameters:
+ - $ref: "#/components/parameters/EnvId"
+ - $ref: "#/components/parameters/MetricName"
+ responses:
+ "200":
+ description: |
+ The response contains a list of available filters for the specified metric, including their supported operators, types, and optional enum values or ranges.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/FilterSpecsResponse"
+ examples:
+ http-requests-filters:
+ summary: HTTP Requests Metric Filters
+ value:
+ data:
+ - name: "API"
+ label: "API"
+ type: "KEYWORD"
+ operators: ["EQ", "IN"]
+ - name: "APPLICATION"
+ label: "Application"
+ type: "KEYWORD"
+ operators: ["EQ", "IN"]
+ - name: "HTTP_STATUS"
+ label: "Status Code"
+ type: "NUMBER"
+ operators: ["EQ", "LTE", "GTE"]
+ range:
+ min: 100
+ max: 600
+ - name: "HTTP_STATUS_CODE_GROUP"
+ label: "Status Code Group"
+ type: "ENUM"
+ operators: ["EQ", "IN"]
+ enumValues: ["1xx", "2xx", "3xx", "4xx", "5xx"]
+ "400":
+ description: Bad request - Invalid metric name for filters.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ "404":
+ description: API type or metric not found.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ "500":
+ description: Internal server error while getting filter specifications for the metric.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+
+ /environments/{envId}/analytics/definition/metrics/{metricName}/facets:
+ get:
+ operationId: queryFacetSpecsForMetric
+ summary: Query available facets for a specific metric
+ description: |
+ Returns a list of available facets that can be used to group data for a specific metric within an API type, including their data types.
+ tags:
+ - definition
+ parameters:
+ - $ref: "#/components/parameters/EnvId"
+ - $ref: "#/components/parameters/MetricName"
+ responses:
+ "200":
+ description: |
+ The response contains a list of available facets for the specified metric that can be used for grouping analytics data, including their data types.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/FacetSpecsResponse"
+ examples:
+ http-requests-facets:
+ summary: HTTP Requests Metric Facets
+ value:
+ data:
+ - name: "API"
+ label: "API"
+ type: "KEYWORD"
+ - name: "APPLICATION"
+ label: "Application"
+ type: "KEYWORD"
+ - name: "GATEWAY"
+ label: "Gateway"
+ type: "KEYWORD"
+
+ "400":
+ description: Bad request - Invalid metric name for facets.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ "500":
+ description: Internal server error while getting facet specifications for the metric.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+
/environments/{envId}/analytics/measures:
post:
operationId: queryMeasures
@@ -49,9 +252,9 @@ paths:
- $ref: "#/components/parameters/EnvId"
requestBody:
description: |
- A measures request is a request to obtain a single set of measures / aggregations, without grouping them by bucket.
+ A measures request is a request to obtain a single set of measures, without grouping them by bucket.
- - Measures requests allow to return aggregated values for the specified metric and aggregations.
+ - Measures requests allow to return aggregated values for the specified metric and measures.
- Available measures can change depending on the required metric.
@@ -72,8 +275,8 @@ paths:
from: "2025-01-01T00:00:00Z"
to: "2025-01-31T23:59:59Z"
metrics:
- - name: "HTTP_REQUEST_COUNT"
- aggregations: ["COUNT", "RPS"]
+ - name: "HTTP_REQUESTS"
+ measures: ["COUNT", "RPS"]
multiple-metrics:
summary: Multiple metrics example
value:
@@ -81,10 +284,10 @@ paths:
from: "2025-01-01T00:00:00Z"
to: "2025-01-31T23:59:59Z"
metrics:
- - name: "HTTP_REQUEST_COUNT"
- aggregations: ["COUNT"]
+ - name: "HTTP_REQUESTS"
+ measures: ["COUNT"]
- name: "HTTP_GATEWAY_RESPONSE_TIME"
- aggregations: ["AVG"]
+ measures: ["AVG"]
responses:
"200":
description: |
@@ -98,20 +301,24 @@ paths:
summary: Single metric response
value:
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
measures:
- COUNT: 2984
- RPS: 45.7
+ - name: "COUNT"
+ value: 2984
+ - name: "RPS"
+ value: 45.7
multiple-metrics:
summary: Multiple metrics response
value:
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
measures:
- COUNT: 2984
+ - name: "COUNT"
+ value: 2984
- name: "HTTP_GATEWAY_RESPONSE_TIME"
measures:
- AVG: 89.5
+ - name: "AVG"
+ value: 89.5
"400":
description: Bad request - Invalid Measures Request.
content:
@@ -157,9 +364,9 @@ paths:
timeRange:
from: "2025-01-01T00:00:00Z"
to: "2025-01-31T23:59:59Z"
+ by: ["API"]
metrics:
- - name: "HTTP_REQUEST_COUNT"
- by: ["API"]
+ - name: "HTTP_REQUESTS"
limit: 1
order:
measure: "COUNT"
@@ -173,14 +380,16 @@ paths:
$ref: "#/components/schemas/FacetsResponse"
example:
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
buckets:
- key: "07ba30d4-7b42-418b-a3f9-2ad684947b82"
measures:
- COUNT: 1234
+ - name: "COUNT"
+ value: 1234
- key: "a0131e80-d886-462a-8c0b-1d205e42cc12"
measures:
- COUNT: 989
+ - name: "COUNT"
+ value: 989
"400":
description: Bad request - Invalid Facets Request.
content:
@@ -229,7 +438,7 @@ paths:
interval: "auto"
by: ["API"]
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
responses:
"200":
description: Time series analytics response
@@ -240,26 +449,30 @@ paths:
example:
interval: "1m"
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
buckets:
- key: "2025-01-01T00:00:00Z"
timestamp: 1735689600000
buckets:
- key: "a5954f6e-1d95-463a-9a41-2442c575d022"
measures:
- COUNT: 1234
+ - name: "COUNT"
+ value: 1234
- key: "12bff449-1556-491c-8e34-c1c93b27e1cb"
measures:
- COUNT: 989
+ - name: "COUNT"
+ value: 989
- key: "2025-01-01T00:01:00Z"
timestamp: 1735689660000
buckets:
- key: "a5954f6e-1d95-463a-9a41-2442c575d022"
measures:
- COUNT: 873
+ - name: "COUNT"
+ value: 873
- key: "12bff449-1556-491c-8e34-c1c93b27e1cb"
measures:
- COUNT: 938
+ - name: "COUNT"
+ value: 938
"400":
description: Bad request - Invalid Time Series Request
content:
@@ -284,21 +497,48 @@ components:
type: string
default: DEFAULT
+ ApiName:
+ name: apiName
+ in: path
+ required: true
+ description: The API type identifier for which to retrieve metrics
+ schema:
+ $ref: "#/components/schemas/ApiName"
+
+ MetricName:
+ name: metricName
+ in: path
+ required: true
+ description: The metric identifier for which to retrieve filter specifications
+ schema:
+ $ref: "#/components/schemas/MetricName"
+
schemas:
+ ApiName:
+ type: string
+ enum:
+ - HTTP_PROXY
+ - MESSAGE
+ - KAFKA
+ - LLM
+ - MCP
+ description: Available API names for analytics queries
+ example: "HTTP_PROXY"
+
MetricName:
type: string
enum:
- HTTP_REQUESTS
- - HTTP_ERROR_RATE
+ - HTTP_ERRORS
- HTTP_REQUEST_CONTENT_LENGTH
- HTTP_RESPONSE_CONTENT_LENGTH
- HTTP_ENDPOINT_RESPONSE_TIME
- HTTP_GATEWAY_RESPONSE_TIME
- HTTP_GATEWAY_LATENCY
- LLM_PROMPT_TOKEN_SENT
- - LLM_PROMPT_TOKEN_RECEIVE
+ - LLM_PROMPT_TOKEN_RECEIVED
- LLM_PROMPT_TOKEN_SENT_COST
- - LLM_PROMPT_TOKEN_RECEIVE_COST
+ - LLM_PROMPT_TOKEN_RECEIVED_COST
- MESSAGE_PAYLOAD_SIZE
- MESSAGES
- MESSAGE_ERRORS
@@ -322,6 +562,7 @@ components:
- SUBSCRIPTIONS
- APIS
description: Available metric names for analytics queries
+
FacetName:
type: string
enum:
@@ -348,9 +589,10 @@ components:
- MESSAGE_OPERATION_TYPE
- KAFKA_TOPIC
- API_STATE
- - API_LIFECYCLE_STATEgst
+ - API_LIFECYCLE_STATE
- API_VISIBILITY
description: Available facet names for grouping analytics data
+
FilterName:
type: string
enum:
@@ -388,76 +630,6 @@ components:
- HTTP_REQUEST_CONTENT_LENGTH
- HTTP_RESPONSE_CONTENT_LENGTH
description: Available filter names for filtering analytics data
- Error:
- description: |
- The generic error object returned by any endpoint of the Gravitee APIM API.
- type: object
- properties:
- httpStatus:
- type: integer
- format: int32
- description: The error code
- example: 400
- message:
- type: string
- description: A global response error message
- example: Bad request
- technicalCode:
- type: string
- description: A technical code to identify the error
- example: invalid.import.definition
- parameters:
- type: object
- description: |
- Parameters add information about the context of the error.
- additionalProperties:
- type: string
- example:
- name: "HTTP_REQUEST_COUNT"
- type: "TIME_SERIES"
- interval: "-1d"
- details:
- type: array
- description: A list of details about the error
- items:
- type: object
- properties:
- message:
- type: string
- description: A fine grained error message regarding a failing property.
- example: Bad request
- location:
- type: string
- description: The json path of the field in error.
- example: updateApi.properties[0].key
- invalidValue:
- oneOf:
- - type: string
- - type: number
- - type: boolean
- - type: array
- - type: object
- description: The invalid value (can be any type)
- example: "-1d"
- example:
- - message: "Interval must be a positive duration (e.g., '1h', '1d', 'auto')"
- location: "requests[0].interval"
- invalidValue: "-1d"
- example:
- httpStatus: 400
- message: "Bad request - Invalid Time Series Request"
- technicalCode: "invalid.analytics.request"
- parameters:
- field: "requests[0].interval"
- value: "-1d"
- interval: "-1d"
- details:
- - message: "Invalid interval value"
- location: "requests[0].interval"
- invalidValue: "-1d"
- - message: "Interval must be a positive duration (e.g., '1h', '1d', 'auto')"
- location: "requests[0].interval"
- invalidValue: "-1d"
Operator:
type: string
@@ -468,7 +640,7 @@ components:
- GTE
description: Filter operator
- Aggregation:
+ MeasureName:
type: string
enum:
- AVG
@@ -480,7 +652,28 @@ components:
- P99
- COUNT
- RPS
- description: Aggregation function
+ - PERCENTAGE
+ description: Measure name identifier. Represents the type of measure/aggregation applied to a metric.
+ example: "COUNT"
+
+ Measure:
+ type: object
+ required:
+ - name
+ - value
+ description: A single measure result containing the measure name and its value.
+ properties:
+ name:
+ $ref: "#/components/schemas/MeasureName"
+ value:
+ oneOf:
+ - type: number
+ - type: string
+ description: The measure value. Can be a number or a string representation of a number.
+ example: 2984
+ example:
+ name: "COUNT"
+ value: 2984
OrderDirection:
type: string
@@ -489,6 +682,279 @@ components:
- DESC
description: Sort order direction
+ MetricType:
+ type: string
+ enum:
+ - COUNTER
+ - GAUGE
+ description: |
+ Metric type identifier.
+
+ - **COUNTER**: A counter metric increases monotonically, representing cumulative values over time. Counters are typically used for counting events or occurrences.
+
+ - **GAUGE**: A gauge metric can increase and decrease over time, representing values that can go up or down. Gauges are typically used for measuring current state or instantaneous values.
+ example: "COUNTER"
+
+ UnitName:
+ type: string
+ enum:
+ - BYTES
+ - MILLISECONDS
+ - NUMBER
+ description: |
+ Unit of measurement for gauge metrics. Specifies the unit in which the metric values are expressed.
+
+ This field is optional and only applicable for GAUGE metrics. COUNTER metrics do not have units.
+ example: "BYTES"
+
+ ApiSpec:
+ description: |
+ An API type specification. Available metrics are defined by the API type.
+ type: object
+ properties:
+ name:
+ $ref: "#/components/schemas/ApiName"
+ label:
+ type: string
+ description: A human-readable label for the API
+ example: "HTTP Proxy"
+ example:
+ name: "HTTP_PROXY"
+ label: "HTTP Proxy"
+
+ FacetSpec:
+ type: object
+ required:
+ - name
+ - label
+ - type
+ description: Facet specification for analytics grouping
+ properties:
+ name:
+ $ref: "#/components/schemas/FacetName"
+ label:
+ type: string
+ description: Human-readable label for the facet
+ example: "API"
+ type:
+ type: string
+ enum:
+ - STRING
+ - KEYWORD
+ - NUMBER
+ description: Data type of the facet
+ example: "KEYWORD"
+ example:
+ name: "API"
+ label: "API"
+ type: "KEYWORD"
+
+ FilterSpec:
+ type: object
+ required:
+ - name
+ - label
+ - type
+ - operators
+ description: Filter specification for filtering analytics. Available filters are defined for a specific metric.
+ properties:
+ name:
+ $ref: "#/components/schemas/FilterName"
+ label:
+ type: string
+ description: Human-readable label for the filter
+ example: "API"
+ type:
+ type: string
+ enum:
+ - KEYWORD
+ - STRING
+ - NUMBER
+ - ENUM
+ description: Data type of the filter
+ example: "KEYWORD"
+ operators:
+ type: array
+ items:
+ $ref: "#/components/schemas/Operator"
+ description: List of supported operators for this filter
+ example: ["EQ", "IN"]
+ enumValues:
+ type: array
+ items:
+ type: string
+ description: Enum values (only applicable for enum type)
+ example: ["GET", "POST", "PUT"]
+ range:
+ type: object
+ properties:
+ min:
+ type: number
+ description: Minimum value
+ example: 100
+ max:
+ type: number
+ description: Maximum value
+ example: 600
+ description: Value range (only applicable for integer/long types)
+ example:
+ min: 100
+ max: 600
+ example:
+ name: "API"
+ label: "API"
+ type: "KEYWORD"
+ operators: ["EQ", "IN"]
+
+ MetricSpec:
+ type: object
+ required:
+ - name
+ - label
+ - apis
+ - type
+ - measures
+ - facets
+ - filters
+ description: Metric specification for analytics queries
+ properties:
+ name:
+ $ref: "#/components/schemas/MetricName"
+ label:
+ type: string
+ description: Human-readable label for the metric
+ example: "HTTP Requests"
+ apis:
+ type: array
+ items:
+ $ref: "#/components/schemas/ApiName"
+ description: List of API types this metric applies to
+ example: ["HTTP_PROXY", "LLM"]
+ type:
+ $ref: "#/components/schemas/MetricType"
+ unit:
+ $ref: "#/components/schemas/UnitName"
+ measures:
+ type: array
+ items:
+ $ref: "#/components/schemas/MeasureName"
+ description: List of supported measures for this metric
+ example: ["COUNT", "RPS"]
+ facets:
+ type: array
+ items:
+ $ref: "#/components/schemas/FacetName"
+ description: List of facets that can be used for grouping this metric
+ example: ["API", "APPLICATION", "GATEWAY"]
+ filters:
+ type: array
+ items:
+ $ref: "#/components/schemas/FilterName"
+ description: List of filters that can be applied to this metric
+ example: ["API", "APPLICATION", "HTTP_STATUS"]
+ example:
+ name: "HTTP_REQUESTS"
+ label: "HTTP Requests"
+ apis: ["HTTP_PROXY", "LLM"]
+ type: "COUNTER"
+ measures: ["COUNT", "RPS"]
+ facets: ["API", "APPLICATION", "GATEWAY"]
+ filters: ["API", "APPLICATION", "HTTP_STATUS"]
+
+ ApiSpecsResponse:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: "#/components/schemas/ApiSpec"
+ description: The response contains a list of available APIs for analytics queries.
+ example:
+ data:
+ - name: "HTTP_PROXY"
+ label: "HTTP Proxy"
+ - name: "MESSAGE"
+ label: "Message"
+ - name: "KAFKA"
+ label: "Kafka"
+ - name: "LLM"
+ label: "LLM"
+ - name: "MCP"
+ label: "MCP"
+
+ FacetSpecsResponse:
+ type: object
+ description: The response contains a list of available facets for grouping a metric.
+ properties:
+ data:
+ type: array
+ items:
+ $ref: "#/components/schemas/FacetSpec"
+ example:
+ data:
+ - name: "API"
+ label: "API"
+ type: "KEYWORD"
+ - name: "APPLICATION"
+ label: "Application"
+ type: "KEYWORD"
+ - name: "GATEWAY"
+ label: "Gateway"
+ type: "KEYWORD"
+
+ FilterSpecsResponse:
+ type: object
+ description: The response contains a list of available filters for filtering analytics data.
+ properties:
+ data:
+ type: array
+ items:
+ $ref: "#/components/schemas/FilterSpec"
+ example:
+ data:
+ - name: "API"
+ label: "API"
+ type: "KEYWORD"
+ operators: ["EQ", "IN"]
+ - name: "HTTP_METHOD"
+ label: "HTTP Method"
+ type: "ENUM"
+ operators: ["EQ", "IN"]
+ enumValues: ["GET", "POST", "PUT"]
+ - name: "HTTP_STATUS"
+ label: "Status Code"
+ type: "NUMBER"
+ operators: ["EQ", "LTE", "GTE"]
+ range:
+ min: 100
+ max: 600
+
+ MetricSpecsResponse:
+ type: object
+ description: The response contains a list of available metrics for a specific API type, including their supported measures, facets, and filters.
+ properties:
+ data:
+ type: array
+ items:
+ $ref: "#/components/schemas/MetricSpec"
+ example:
+ data:
+ - name: "HTTP_REQUESTS"
+ label: "HTTP Requests"
+ apis: ["HTTP_PROXY", "LLM"]
+ type: "COUNTER"
+ measures: ["COUNT", "RPS"]
+ facets: ["API", "APPLICATION", "GATEWAY"]
+ filters: ["API", "APPLICATION", "HTTP_STATUS"]
+ - name: "HTTP_GATEWAY_RESPONSE_TIME"
+ label: "Gateway response time"
+ apis: ["HTTP_PROXY", "LLM"]
+ type: "GAUGE"
+ unit: "MILLISECONDS"
+ measures: ["AVG", "MIN", "MAX", "P50", "P90", "P95", "P99"]
+ facets: ["API", "APPLICATION", "GATEWAY"]
+ filters: ["API", "APPLICATION", "HTTP_STATUS"]
+
Interval:
type: string
description: Time interval for time series queries (e.g., "1m", "auto").
@@ -531,6 +997,10 @@ components:
- type: string
- type: number
- type: array
+ items:
+ oneOf:
+ - type: string
+ - type: number
items:
oneOf:
- type: string
@@ -571,11 +1041,11 @@ components:
properties:
name:
$ref: "#/components/schemas/MetricName"
- aggregations:
+ measures:
type: array
items:
- $ref: "#/components/schemas/Aggregation"
- description: List of aggregations to apply
+ $ref: "#/components/schemas/MeasureName"
+ description: List of measures to compute for this metric
filters:
type: array
items:
@@ -592,7 +1062,7 @@ components:
value: "7b6ebef3-6236-4ac5-815e-d3dcef83df5d"
metrics:
- name: "HTTP_GATEWAY_RESPONSE_TIME"
- aggregations: ["P90", "P95", "P99"]
+ measures: ["P90", "P95", "P99"]
FacetsRequest:
type: object
@@ -656,7 +1126,7 @@ components:
operator: "EQ"
value: "7b6ebef3-6236-4ac5-815e-d3dcef83df5d"
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
TimeSeriesRequest:
type: object
@@ -705,9 +1175,10 @@ components:
operator: "EQ"
value: "7b6ebef3-6236-4ac5-815e-d3dcef83df5d"
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
+ measures: ["COUNT"]
- name: "HTTP_GATEWAY_RESPONSE_TIME"
- aggregations: ["AVG"]
+ measures: ["AVG"]
MetricRequest:
type: object
@@ -717,11 +1188,11 @@ components:
properties:
name:
$ref: "#/components/schemas/MetricName"
- aggregations:
+ measures:
type: array
items:
- $ref: "#/components/schemas/Aggregation"
- description: List of aggregations to apply
+ $ref: "#/components/schemas/MeasureName"
+ description: List of measures to compute for this metric
filters:
type: array
items:
@@ -729,40 +1200,18 @@ components:
description: Request-level filters
Measures:
- type: object
- properties:
- AVG:
- type: number
- description: Average value
- MIN:
- type: number
- description: Minimum value
- MAX:
- type: number
- description: Maximum value
- P50:
- type: number
- description: 50th percentile value
- P90:
- type: number
- description: 90th percentile value
- P95:
- type: number
- description: 95th percentile value
- P99:
- type: number
- description: 99th percentile value
- COUNT:
- type: number
- description: Count value
- RPS:
- type: number
- description: Requests per second value
- additionalProperties: false
+ type: array
+ items:
+ $ref: "#/components/schemas/Measure"
description: |
- Map of aggregation name to measure value (e.g., {"COUNT": 2984, "RPS": 45.7, "P90": 89.9})
- The measures object is always represented as the leaf node of the buckets tree for other types of analytics requests.
- Only valid aggregation names from the Aggregation enum are allowed as keys.
+ Array of measure results. Each measure contains a name and value.
+ The measures array is always represented as the leaf node of the buckets tree for other types of analytics requests.
+ Example: [{"name": "COUNT", "value": 2984}, {"name": "RPS", "value": 45.7}]
+ example:
+ - name: "COUNT"
+ value: 2984
+ - name: "RPS"
+ value: 45.7
Bucket:
oneOf:
@@ -783,7 +1232,7 @@ components:
properties:
key:
type: string
- description: Bucket key (facet value for facet buckets, timestamp string for time series buckets)
+ description: The bucket key is the identifier of the bucket.
buckets:
$ref: "#/components/schemas/BucketList"
description: A bucket can contain either measures (leaf node) or nested buckets (for grouping)
@@ -821,11 +1270,11 @@ components:
properties:
key:
type: string
- description: Bucket key (facet value for facet buckets, timestamp string for time series buckets)
+ description: The bucket key is an ISO 8601 timestamp string.
timestamp:
type: integer
format: int64
- description: Timestamp in milliseconds since Unix epoch (optional)
+ description: Timestamp in milliseconds since Unix epoch
example: 1735689600000
buckets:
$ref: "#/components/schemas/BucketList"
@@ -857,9 +1306,10 @@ components:
description: List of measure metric responses
example:
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
measures:
- COUNT: 2984
+ - name: "COUNT"
+ value: 2984
FacetsResponse:
type: object
@@ -881,19 +1331,22 @@ components:
description: List of facet metric responses. Each metric can have either measures or buckets for grouping.
example:
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
buckets:
- key: "49ddd7db-35d6-497e-b497-2dc16af0cdcc"
buckets:
- key: "e65289b0-35b2-40e7-8d41-711fba7fdc96"
measures:
- RPS: 45.7
+ - name: "RPS"
+ value: 45.7
- key: "4c65d87a-008a-4a4c-b11a-a38b7a662fcc"
measures:
- RPS: 34.9
- - key: "71091fd9-a6a9-4c7f-afdb-d837ea28058c"
- measures:
- COUNT: 989
+ - name: "RPS"
+ value: 34.9
+ - key: "71091fd9-a6a9-4c7f-afdb-d837ea28058c"
+ measures:
+ - name: "RPS"
+ value: 989
TimeSeriesResponse:
type: object
@@ -919,24 +1372,104 @@ components:
example:
interval: "1m"
metrics:
- - name: "HTTP_REQUEST_COUNT"
+ - name: "HTTP_REQUESTS"
buckets:
- key: "2025-01-01T00:00:00Z"
timestamp: 1735689600000
buckets:
- key: "a5954f6e-1d95-463a-9a41-2442c575d022"
measures:
- COUNT: 1234
+ - name: "COUNT"
+ value: 1234
- key: "12bff449-1556-491c-8e34-c1c93b27e1cb"
measures:
- COUNT: 989
+ - name: "COUNT"
+ value: 989
- key: "2025-01-01T00:01:00Z"
timestamp: 1735689660000
buckets:
- key: "a5954f6e-1d95-463a-9a41-2442c575d022"
measures:
- COUNT: 873
+ - name: "COUNT"
+ value: 873
- key: "12bff449-1556-491c-8e34-c1c93b27e1cb"
measures:
- COUNT: 938
+ - name: "COUNT"
+ value: 938
+ Error:
+ description: |
+ The generic error object returned by any endpoint of the Gravitee APIM API.
+ type: object
+ properties:
+ httpStatus:
+ type: integer
+ format: int32
+ description: The error code
+ example: 400
+ message:
+ type: string
+ description: A global response error message
+ example: Bad request
+ technicalCode:
+ type: string
+ description: A technical code to identify the error
+ example: invalid.import.definition
+ parameters:
+ type: object
+ description: |
+ Parameters add information about the context of the error.
+ additionalProperties:
+ type: string
+ example:
+ name: "HTTP_REQUESTS"
+ type: "TIME_SERIES"
+ interval: "-1d"
+ details:
+ type: array
+ description: A list of details about the error
+ items:
+ type: object
+ properties:
+ message:
+ type: string
+ description: A fine grained error message regarding a failing property.
+ example: Bad request
+ location:
+ type: string
+ description: The json path of the field in error.
+ example: updateApi.properties[0].key
+ invalidValue:
+ oneOf:
+ - type: string
+ - type: number
+ - type: boolean
+ - type: array
+ items:
+ oneOf:
+ - type: string
+ - type: number
+ - type: boolean
+ - type: object
+ - type: object
+ description: The invalid value (can be any type)
+ example: "-1d"
+ example:
+ - message: "Interval must be a positive duration (e.g., '1h', '1d', 'auto')"
+ location: "requests[0].interval"
+ invalidValue: "-1d"
+ example:
+ httpStatus: 400
+ message: "Bad request - Invalid Time Series Request"
+ technicalCode: "invalid.analytics.request"
+ parameters:
+ field: "requests[0].interval"
+ value: "-1d"
+ interval: "-1d"
+ details:
+ - message: "Invalid interval value"
+ location: "requests[0].interval"
+ invalidValue: "-1d"
+ - message: "Interval must be a positive duration (e.g., '1h', '1d', 'auto')"
+ location: "requests[0].interval"
+ invalidValue: "-1d"
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/resource/analytics/definition/AnalyticsDefinitionResourceTest.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/resource/analytics/definition/AnalyticsDefinitionResourceTest.java
new file mode 100644
index 00000000000..8f62106ccb9
--- /dev/null
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/resource/analytics/definition/AnalyticsDefinitionResourceTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.rest.api.management.v2.rest.resource.analytics.definition;
+
+import static assertions.MAPIAssertions.assertThat;
+
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.ApiSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.FacetSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.FilterSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.model.analytics.engine.MetricSpecsResponse;
+import io.gravitee.rest.api.management.v2.rest.resource.api.ApiResourceTest;
+import org.junit.jupiter.api.DisplayNameGeneration;
+import org.junit.jupiter.api.DisplayNameGenerator;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Antoine CORDIER (antoine.cordier at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
+class AnalyticsDefinitionResourceTest extends ApiResourceTest {
+
+ @Override
+ protected String contextPath() {
+ return "/environments/" + ENVIRONMENT + "/analytics/definition";
+ }
+
+ @Test
+ void should_return_api_specs() {
+ var response = rootTarget().path("apis").request().get();
+
+ assertThat(response)
+ .hasStatus(200)
+ .asEntity(ApiSpecsResponse.class)
+ .extracting(ApiSpecsResponse::getData)
+ .satisfies(apis -> assertThat(apis).isNotEmpty());
+ }
+
+ @Test
+ void should_return_api_metrics() {
+ var response = rootTarget().path("apis").path("HTTP_PROXY").path("metrics").request().get();
+
+ assertThat(response)
+ .hasStatus(200)
+ .asEntity(MetricSpecsResponse.class)
+ .extracting(MetricSpecsResponse::getData)
+ .satisfies(metrics -> assertThat(metrics).isNotEmpty());
+ }
+
+ @Test
+ void should_return_metric_filters() {
+ var response = rootTarget().path("metrics").path("HTTP_REQUESTS").path("filters").request().get();
+
+ assertThat(response)
+ .hasStatus(200)
+ .asEntity(FilterSpecsResponse.class)
+ .extracting(FilterSpecsResponse::getData)
+ .satisfies(filters -> assertThat(filters).isNotEmpty());
+ }
+
+ @Test
+ void should_return_metric_facets() {
+ var response = rootTarget().path("metrics").path("HTTP_REQUESTS").path("facets").request().get();
+
+ assertThat(response)
+ .hasStatus(200)
+ .asEntity(FacetSpecsResponse.class)
+ .extracting(FacetSpecsResponse::getData)
+ .satisfies(facets -> assertThat(facets).isNotEmpty());
+ }
+
+ @Test
+ void should_return_error_for_unknown_metric_facets() {
+ var response = rootTarget().path("metrics").path("FOO").path("facets").request().get();
+
+ assertThat(response).hasStatus(400);
+ }
+
+ @Test
+ void should_return_error_for_unknown_metric_filters() {
+ var response = rootTarget().path("metrics").path("BAR").path("facets").request().get();
+
+ assertThat(response).hasStatus(400);
+ }
+
+ @Test
+ void should_return_error_for_unknown_api_metrics() {
+ var response = rootTarget().path("apis").path("BAZ").path("metrics").request().get();
+
+ assertThat(response).hasStatus(400);
+ }
+}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/spring/ResourceContextConfiguration.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/spring/ResourceContextConfiguration.java
index c1a10a74810..df7d154d488 100644
--- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/spring/ResourceContextConfiguration.java
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management-v2/gravitee-apim-rest-api-management-v2-rest/src/test/java/io/gravitee/rest/api/management/v2/rest/spring/ResourceContextConfiguration.java
@@ -40,6 +40,11 @@
import inmemory.SharedPolicyGroupCrudServiceInMemory;
import inmemory.UserDomainServiceInMemory;
import inmemory.spring.InMemoryConfiguration;
+import io.gravitee.apim.core.analytics.domain_service.engine.definition.AnalyticsDefinition;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiMetricsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiSpecsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFacetsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFiltersUseCase;
import io.gravitee.apim.core.api.domain_service.ApiExportDomainService;
import io.gravitee.apim.core.api.domain_service.ApiImportDomainService;
import io.gravitee.apim.core.api.domain_service.ApiMetadataDecoderDomainService;
@@ -135,6 +140,7 @@
import io.gravitee.apim.core.user.domain_service.UserDomainService;
import io.gravitee.apim.infra.adapter.SubscriptionAdapter;
import io.gravitee.apim.infra.adapter.SubscriptionAdapterImpl;
+import io.gravitee.apim.infra.domain_service.analytics.AnalyticsDefinitionDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.application.ValidateApplicationSettingsDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.documentation.ValidatePageSourceDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.group.ValidateGroupCRDDomainServiceImpl;
@@ -868,4 +874,29 @@ public CockpitPromotionServiceProvider cockpitPromotionServiceProvider() {
public CreatePromotionUseCase createPromotionUseCase() {
return mock(CreatePromotionUseCase.class);
}
+
+ @Bean
+ public AnalyticsDefinition analyticsDefinitionDomainService() {
+ return new AnalyticsDefinitionDomainServiceImpl();
+ }
+
+ @Bean
+ public GetApiSpecsUseCase getApiSpecsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetApiSpecsUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetApiMetricsUseCase getApiMetricsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetApiMetricsUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetMetricFiltersUseCase getMetricFiltersUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetMetricFiltersUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetMetricFacetsUseCase getMetricFacetsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetMetricFacetsUseCase(analyticsDefinition);
+ }
}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-management/gravitee-apim-rest-api-management-rest/src/test/java/io/gravitee/rest/api/management/rest/spring/ResourceContextConfiguration.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-management/gravitee-apim-rest-api-management-rest/src/test/java/io/gravitee/rest/api/management/rest/spring/ResourceContextConfiguration.java
index 24f9d999ea2..0aaa0325b63 100644
--- a/gravitee-apim-rest-api/gravitee-apim-rest-api-management/gravitee-apim-rest-api-management-rest/src/test/java/io/gravitee/rest/api/management/rest/spring/ResourceContextConfiguration.java
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-management/gravitee-apim-rest-api-management-rest/src/test/java/io/gravitee/rest/api/management/rest/spring/ResourceContextConfiguration.java
@@ -32,6 +32,11 @@
import inmemory.SharedPolicyGroupCrudServiceInMemory;
import inmemory.spring.InMemoryConfiguration;
import io.gravitee.apim.core.access_point.query_service.AccessPointQueryService;
+import io.gravitee.apim.core.analytics.domain_service.engine.definition.AnalyticsDefinition;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiMetricsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiSpecsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFacetsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFiltersUseCase;
import io.gravitee.apim.core.api.domain_service.ApiExportDomainService;
import io.gravitee.apim.core.api.domain_service.ApiImportDomainService;
import io.gravitee.apim.core.api.domain_service.ApiMetadataDecoderDomainService;
@@ -109,6 +114,7 @@
import io.gravitee.apim.core.subscription.use_case.ImportSubscriptionSpecUseCase;
import io.gravitee.apim.infra.adapter.SubscriptionAdapter;
import io.gravitee.apim.infra.adapter.SubscriptionAdapterImpl;
+import io.gravitee.apim.infra.domain_service.analytics.AnalyticsDefinitionDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.api.ApiHostValidatorDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.application.ValidateApplicationSettingsDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.documentation.ValidatePageSourceDomainServiceImpl;
@@ -1006,4 +1012,29 @@ public CockpitPromotionServiceProvider cockpitPromotionServiceProvider() {
public CreatePromotionUseCase createPromotionUseCase() {
return mock(CreatePromotionUseCase.class);
}
+
+ @Bean
+ public AnalyticsDefinition analyticsDefinitionDomainService() {
+ return new AnalyticsDefinitionDomainServiceImpl();
+ }
+
+ @Bean
+ public GetApiSpecsUseCase getApiSpecsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetApiSpecsUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetApiMetricsUseCase getApiMetricsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetApiMetricsUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetMetricFiltersUseCase getMetricFiltersUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetMetricFiltersUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetMetricFacetsUseCase getMetricFacetsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetMetricFacetsUseCase(analyticsDefinition);
+ }
}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-portal/gravitee-apim-rest-api-portal-rest/src/test/java/io/gravitee/rest/api/portal/rest/spring/ResourceContextConfiguration.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-portal/gravitee-apim-rest-api-portal-rest/src/test/java/io/gravitee/rest/api/portal/rest/spring/ResourceContextConfiguration.java
index 7ab233bd19f..d53fe452e3a 100644
--- a/gravitee-apim-rest-api/gravitee-apim-rest-api-portal/gravitee-apim-rest-api-portal-rest/src/test/java/io/gravitee/rest/api/portal/rest/spring/ResourceContextConfiguration.java
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-portal/gravitee-apim-rest-api-portal-rest/src/test/java/io/gravitee/rest/api/portal/rest/spring/ResourceContextConfiguration.java
@@ -31,6 +31,11 @@
import inmemory.SharedPolicyGroupCrudServiceInMemory;
import inmemory.spring.InMemoryConfiguration;
import io.gravitee.apim.core.access_point.query_service.AccessPointQueryService;
+import io.gravitee.apim.core.analytics.domain_service.engine.definition.AnalyticsDefinition;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiMetricsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetApiSpecsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFacetsUseCase;
+import io.gravitee.apim.core.analytics.use_case.engine.definition.GetMetricFiltersUseCase;
import io.gravitee.apim.core.api.domain_service.ApiExportDomainService;
import io.gravitee.apim.core.api.domain_service.ApiImportDomainService;
import io.gravitee.apim.core.api.domain_service.ApiMetadataDecoderDomainService;
@@ -107,6 +112,7 @@
import io.gravitee.apim.core.subscription.use_case.ImportSubscriptionSpecUseCase;
import io.gravitee.apim.infra.adapter.SubscriptionAdapter;
import io.gravitee.apim.infra.adapter.SubscriptionAdapterImpl;
+import io.gravitee.apim.infra.domain_service.analytics.AnalyticsDefinitionDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.api.ApiHostValidatorDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.application.ValidateApplicationSettingsDomainServiceImpl;
import io.gravitee.apim.infra.domain_service.documentation.ValidatePageSourceDomainServiceImpl;
@@ -967,4 +973,29 @@ public CockpitPromotionServiceProvider cockpitPromotionServiceProvider() {
public CreatePromotionUseCase createPromotionUseCase() {
return mock(CreatePromotionUseCase.class);
}
+
+ @Bean
+ public AnalyticsDefinition analyticsDefinitionDomainService() {
+ return new AnalyticsDefinitionDomainServiceImpl();
+ }
+
+ @Bean
+ public GetApiSpecsUseCase getApiSpecsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetApiSpecsUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetApiMetricsUseCase getApiMetricsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetApiMetricsUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetMetricFiltersUseCase getMetricFiltersUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetMetricFiltersUseCase(analyticsDefinition);
+ }
+
+ @Bean
+ public GetMetricFacetsUseCase getMetricFacetsUseCase(AnalyticsDefinition analyticsDefinition) {
+ return new GetMetricFacetsUseCase(analyticsDefinition);
+ }
}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/domain_service/engine/definition/AnalyticsDefinition.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/domain_service/engine/definition/AnalyticsDefinition.java
new file mode 100644
index 00000000000..8a6076b26c4
--- /dev/null
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/domain_service/engine/definition/AnalyticsDefinition.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.apim.core.analytics.domain_service.engine.definition;
+
+import io.gravitee.apim.core.analytics.model.engine.definition.ApiSpec;
+import io.gravitee.apim.core.analytics.model.engine.definition.FacetSpec;
+import io.gravitee.apim.core.analytics.model.engine.definition.FilterSpec;
+import io.gravitee.apim.core.analytics.model.engine.definition.MetricSpec;
+import io.gravitee.apim.core.exception.ValidationDomainException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public interface AnalyticsDefinition {
+ List getApis();
+
+ List getMetrics(ApiSpec.Name apiSpecName);
+
+ List getFilters(MetricSpec.Name metricSpecName);
+
+ List getFacets(MetricSpec.Name metricSpecName);
+
+ default ApiSpec.Name validateApiName(String apiName) {
+ try {
+ return ApiSpec.Name.valueOf(apiName);
+ } catch (IllegalArgumentException e) {
+ throw new ValidationDomainException(
+ "Invalid api name",
+ Map.of("invalidName", apiName, "validNames", Arrays.toString(ApiSpec.Name.values()))
+ );
+ }
+ }
+
+ default MetricSpec.Name validateMetricName(String metricName) {
+ try {
+ return MetricSpec.Name.valueOf(metricName);
+ } catch (IllegalArgumentException e) {
+ throw new ValidationDomainException(
+ "Invalid metric name",
+ Map.of("invalidName", metricName, "validNames", Arrays.toString(MetricSpec.Name.values()))
+ );
+ }
+ }
+}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/AnalyticsDefinition.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/AnalyticsDefinition.java
new file mode 100644
index 00000000000..001ba29c4c3
--- /dev/null
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/AnalyticsDefinition.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright © 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.apim.core.analytics.model.engine.definition;
+
+public record AnalyticsDefinition(AnalyticsDefinitionSpec spec) {}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/AnalyticsDefinitionSpec.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/AnalyticsDefinitionSpec.java
new file mode 100644
index 00000000000..a3dfbfe7a27
--- /dev/null
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/AnalyticsDefinitionSpec.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright © 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.apim.core.analytics.model.engine.definition;
+
+import java.util.List;
+
+public record AnalyticsDefinitionSpec(List apis, List metrics, List filters, List facets) {}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/ApiSpec.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/ApiSpec.java
new file mode 100644
index 00000000000..79b6dbe241f
--- /dev/null
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/ApiSpec.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.apim.core.analytics.model.engine.definition;
+
+public record ApiSpec(Name name, String label) {
+ public enum Name {
+ HTTP_PROXY,
+ MESSAGE,
+ KAFKA,
+ LLM,
+ MCP,
+ }
+}
diff --git a/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/FacetSpec.java b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/FacetSpec.java
new file mode 100644
index 00000000000..ca2851a9f75
--- /dev/null
+++ b/gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/analytics/model/engine/definition/FacetSpec.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.apim.core.analytics.model.engine.definition;
+
+import java.util.List;
+
+public record FacetSpec(Name name, String label, String type, List