From 1eba57b2509af2779e7a9043e8018b7ec4334d14 Mon Sep 17 00:00:00 2001 From: Nikhil Mantri Date: Thu, 8 Jan 2026 01:48:28 +0530 Subject: [PATCH] chore: add Open API spec defs for metrics explorer (#9934) --- docs/api/openapi.yml | 652 ++++++++++++++++++ .../signozapiserver/metricsexplorer.go | 166 +++++ pkg/apiserver/signozapiserver/provider.go | 65 +- pkg/query-service/app/http_handler.go | 9 - pkg/ruler/signozruler/provider.go | 7 +- pkg/signoz/openapi.go | 2 + pkg/signoz/provider.go | 6 +- pkg/signoz/provider_test.go | 4 +- pkg/signoz/signoz.go | 8 +- 9 files changed, 871 insertions(+), 48 deletions(-) create mode 100644 pkg/apiserver/signozapiserver/metricsexplorer.go diff --git a/docs/api/openapi.yml b/docs/api/openapi.yml index b3d5c6ff59..eade525528 100644 --- a/docs/api/openapi.yml +++ b/docs/api/openapi.yml @@ -2067,6 +2067,431 @@ paths: summary: Get features tags: - features + /api/v2/metric/alerts: + get: + deprecated: false + description: This endpoint returns associated alerts for a specified metric + operationId: GetMetricAlerts + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesMetricAlertsResponse' + status: + type: string + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Get metric alerts + tags: + - metrics + /api/v2/metric/dashboards: + get: + deprecated: false + description: This endpoint returns associated dashboards for a specified metric + operationId: GetMetricDashboards + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesMetricDashboardsResponse' + status: + type: string + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Get metric dashboards + tags: + - metrics + /api/v2/metric/highlights: + get: + deprecated: false + description: This endpoint returns highlights like number of datapoints, totaltimeseries, + active time series, last received time for a specified metric + operationId: GetMetricHighlights + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesMetricHighlightsResponse' + status: + type: string + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Get metric highlights + tags: + - metrics + /api/v2/metrics/{metric_name}/metadata: + post: + deprecated: false + description: This endpoint helps to update metadata information like metric + description, unit, type, temporality, monotonicity for a specified metric + operationId: UpdateMetricMetadata + parameters: + - in: path + name: metric_name + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MetricsexplorertypesUpdateMetricMetadataRequest' + responses: + "200": + content: + application/json: + schema: + type: string + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - EDITOR + - tokenizer: + - EDITOR + summary: Update metric metadata + tags: + - metrics + /api/v2/metrics/attributes: + post: + deprecated: false + description: This endpoint returns attribute keys and their unique values for + a specified metric + operationId: GetMetricAttributes + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MetricsexplorertypesMetricAttributesRequest' + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesMetricAttributesResponse' + status: + type: string + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Get metric attributes + tags: + - metrics + /api/v2/metrics/metadata: + get: + deprecated: false + description: This endpoint returns metadata information like metric description, + unit, type, temporality, monotonicity for a specified metric + operationId: GetMetricMetadata + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesMetricMetadata' + status: + type: string + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Not Found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Get metric metadata + tags: + - metrics + /api/v2/metrics/stats: + post: + deprecated: false + description: This endpoint provides list of metrics with their number of samples + and timeseries for the given time range + operationId: GetMetricsStats + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MetricsexplorertypesStatsRequest' + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesStatsResponse' + status: + type: string + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Get metrics statistics + tags: + - metrics + /api/v2/metrics/treemap: + post: + deprecated: false + description: This endpoint returns a treemap visualization showing the proportional + distribution of metrics by sample count or time series count + operationId: GetMetricsTreemap + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MetricsexplorertypesTreemapRequest' + responses: + "200": + content: + application/json: + schema: + properties: + data: + $ref: '#/components/schemas/MetricsexplorertypesTreemapResponse' + status: + type: string + type: object + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Bad Request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Forbidden + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/RenderErrorResponse' + description: Internal Server Error + security: + - api_key: + - VIEWER + - tokenizer: + - VIEWER + summary: Get metrics treemap + tags: + - metrics /api/v2/orgs/me: get: deprecated: false @@ -2586,6 +3011,202 @@ components: nullable: true type: object type: object + MetricsexplorertypesMetricAlert: + properties: + alertId: + type: string + alertName: + type: string + type: object + MetricsexplorertypesMetricAlertsResponse: + properties: + alerts: + items: + $ref: '#/components/schemas/MetricsexplorertypesMetricAlert' + nullable: true + type: array + type: object + MetricsexplorertypesMetricAttribute: + properties: + key: + type: string + valueCount: + minimum: 0 + type: integer + values: + items: + type: string + nullable: true + type: array + type: object + MetricsexplorertypesMetricAttributesRequest: + properties: + end: + nullable: true + type: integer + metricName: + type: string + start: + nullable: true + type: integer + type: object + MetricsexplorertypesMetricAttributesResponse: + properties: + attributes: + items: + $ref: '#/components/schemas/MetricsexplorertypesMetricAttribute' + nullable: true + type: array + totalKeys: + format: int64 + type: integer + type: object + MetricsexplorertypesMetricDashboard: + properties: + dashboardId: + type: string + dashboardName: + type: string + widgetId: + type: string + widgetName: + type: string + type: object + MetricsexplorertypesMetricDashboardsResponse: + properties: + dashboards: + items: + $ref: '#/components/schemas/MetricsexplorertypesMetricDashboard' + nullable: true + type: array + type: object + MetricsexplorertypesMetricHighlightsResponse: + properties: + activeTimeSeries: + minimum: 0 + type: integer + dataPoints: + minimum: 0 + type: integer + lastReceived: + minimum: 0 + type: integer + totalTimeSeries: + minimum: 0 + type: integer + type: object + MetricsexplorertypesMetricMetadata: + properties: + description: + type: string + isMonotonic: + type: boolean + temporality: + type: string + type: + type: string + unit: + type: string + type: object + MetricsexplorertypesStat: + properties: + description: + type: string + metricName: + type: string + samples: + minimum: 0 + type: integer + timeseries: + minimum: 0 + type: integer + type: + type: string + unit: + type: string + type: object + MetricsexplorertypesStatsRequest: + properties: + end: + format: int64 + type: integer + filter: + $ref: '#/components/schemas/Querybuildertypesv5Filter' + limit: + type: integer + offset: + type: integer + orderBy: + $ref: '#/components/schemas/Querybuildertypesv5OrderBy' + start: + format: int64 + type: integer + type: object + MetricsexplorertypesStatsResponse: + properties: + metrics: + items: + $ref: '#/components/schemas/MetricsexplorertypesStat' + nullable: true + type: array + total: + minimum: 0 + type: integer + type: object + MetricsexplorertypesTreemapEntry: + properties: + metricName: + type: string + percentage: + format: double + type: number + totalValue: + minimum: 0 + type: integer + type: object + MetricsexplorertypesTreemapRequest: + properties: + end: + format: int64 + type: integer + filter: + $ref: '#/components/schemas/Querybuildertypesv5Filter' + limit: + type: integer + mode: + type: string + start: + format: int64 + type: integer + type: object + MetricsexplorertypesTreemapResponse: + properties: + samples: + items: + $ref: '#/components/schemas/MetricsexplorertypesTreemapEntry' + nullable: true + type: array + timeseries: + items: + $ref: '#/components/schemas/MetricsexplorertypesTreemapEntry' + nullable: true + type: array + type: object + MetricsexplorertypesUpdateMetricMetadataRequest: + properties: + description: + type: string + isMonotonic: + type: boolean + metricName: + type: string + temporality: + type: string + type: + type: string + unit: + type: string + type: object PreferencetypesPreference: properties: allowedScopes: @@ -2647,6 +3268,35 @@ components: minimum: 0 type: integer type: object + Querybuildertypesv5Filter: + properties: + expression: + type: string + type: object + Querybuildertypesv5OrderBy: + properties: + direction: + type: string + key: + $ref: '#/components/schemas/Querybuildertypesv5OrderByKey' + type: object + Querybuildertypesv5OrderByKey: + properties: + '-': + $ref: '#/components/schemas/TelemetrytypesJSONDataType' + description: + type: string + fieldContext: + type: string + fieldDataType: + type: string + name: + type: string + signal: + type: string + unit: + type: string + type: object Querybuildertypesv5QueryData: properties: results: @@ -2688,6 +3338,8 @@ components: status: type: string type: object + TelemetrytypesJSONDataType: + type: object TypesChangePasswordRequest: properties: newPassword: diff --git a/pkg/apiserver/signozapiserver/metricsexplorer.go b/pkg/apiserver/signozapiserver/metricsexplorer.go new file mode 100644 index 0000000000..f40e095fe0 --- /dev/null +++ b/pkg/apiserver/signozapiserver/metricsexplorer.go @@ -0,0 +1,166 @@ +package signozapiserver + +import ( + "net/http" + + "github.com/SigNoz/signoz/pkg/http/handler" + "github.com/SigNoz/signoz/pkg/types" + "github.com/SigNoz/signoz/pkg/types/metricsexplorertypes" + "github.com/gorilla/mux" +) + +func (provider *provider) addMetricsExplorerV2Routes(router *mux.Router) error { + if err := router.Handle("/api/v2/metrics/stats", handler.New( + provider.authZ.ViewAccess(provider.metricsExplorerHandler.GetStats), + handler.OpenAPIDef{ + ID: "GetMetricsStats", + Tags: []string{"metrics"}, + Summary: "Get metrics statistics", + Description: "This endpoint provides list of metrics with their number of samples and timeseries for the given time range", + Request: new(metricsexplorertypes.StatsRequest), + RequestContentType: "application/json", + Response: new(metricsexplorertypes.StatsResponse), + ResponseContentType: "application/json", + SuccessStatusCode: http.StatusOK, + ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError}, + Deprecated: false, + SecuritySchemes: newSecuritySchemes(types.RoleViewer), + })).Methods(http.MethodPost).GetError(); err != nil { + return err + } + + if err := router.Handle("/api/v2/metrics/treemap", handler.New( + provider.authZ.ViewAccess(provider.metricsExplorerHandler.GetTreemap), + handler.OpenAPIDef{ + ID: "GetMetricsTreemap", + Tags: []string{"metrics"}, + Summary: "Get metrics treemap", + Description: "This endpoint returns a treemap visualization showing the proportional distribution of metrics by sample count or time series count", + Request: new(metricsexplorertypes.TreemapRequest), + RequestContentType: "application/json", + Response: new(metricsexplorertypes.TreemapResponse), + ResponseContentType: "application/json", + SuccessStatusCode: http.StatusOK, + ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError}, + Deprecated: false, + SecuritySchemes: newSecuritySchemes(types.RoleViewer), + })).Methods(http.MethodPost).GetError(); err != nil { + return err + } + + if err := router.Handle("/api/v2/metrics/attributes", handler.New( + provider.authZ.ViewAccess(provider.metricsExplorerHandler.GetMetricAttributes), + handler.OpenAPIDef{ + ID: "GetMetricAttributes", + Tags: []string{"metrics"}, + Summary: "Get metric attributes", + Description: "This endpoint returns attribute keys and their unique values for a specified metric", + Request: new(metricsexplorertypes.MetricAttributesRequest), + RequestContentType: "application/json", + Response: new(metricsexplorertypes.MetricAttributesResponse), + ResponseContentType: "application/json", + SuccessStatusCode: http.StatusOK, + ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError}, + Deprecated: false, + SecuritySchemes: newSecuritySchemes(types.RoleViewer), + })).Methods(http.MethodPost).GetError(); err != nil { + return err + } + + if err := router.Handle("/api/v2/metrics/metadata", handler.New( + provider.authZ.ViewAccess(provider.metricsExplorerHandler.GetMetricMetadata), + handler.OpenAPIDef{ + ID: "GetMetricMetadata", + Tags: []string{"metrics"}, + Summary: "Get metric metadata", + Description: "This endpoint returns metadata information like metric description, unit, type, temporality, monotonicity for a specified metric", + Request: nil, + RequestContentType: "", + Response: new(metricsexplorertypes.MetricMetadata), + ResponseContentType: "application/json", + SuccessStatusCode: http.StatusOK, + ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusNotFound, http.StatusInternalServerError}, + Deprecated: false, + SecuritySchemes: newSecuritySchemes(types.RoleViewer), + })).Methods(http.MethodGet).GetError(); err != nil { + return err + } + + if err := router.Handle("/api/v2/metrics/{metric_name}/metadata", handler.New( + provider.authZ.EditAccess(provider.metricsExplorerHandler.UpdateMetricMetadata), + handler.OpenAPIDef{ + ID: "UpdateMetricMetadata", + Tags: []string{"metrics"}, + Summary: "Update metric metadata", + Description: "This endpoint helps to update metadata information like metric description, unit, type, temporality, monotonicity for a specified metric", + Request: new(metricsexplorertypes.UpdateMetricMetadataRequest), + RequestContentType: "application/json", + Response: nil, + ResponseContentType: "application/json", + SuccessStatusCode: http.StatusOK, + ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError}, + Deprecated: false, + SecuritySchemes: newSecuritySchemes(types.RoleEditor), + })).Methods(http.MethodPost).GetError(); err != nil { + return err + } + + if err := router.Handle("/api/v2/metric/highlights", handler.New( + provider.authZ.ViewAccess(provider.metricsExplorerHandler.GetMetricHighlights), + handler.OpenAPIDef{ + ID: "GetMetricHighlights", + Tags: []string{"metrics"}, + Summary: "Get metric highlights", + Description: "This endpoint returns highlights like number of datapoints, totaltimeseries, active time series, last received time for a specified metric", + Request: nil, + RequestContentType: "", + Response: new(metricsexplorertypes.MetricHighlightsResponse), + ResponseContentType: "application/json", + SuccessStatusCode: http.StatusOK, + ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError}, + Deprecated: false, + SecuritySchemes: newSecuritySchemes(types.RoleViewer), + })).Methods(http.MethodGet).GetError(); err != nil { + return err + } + + if err := router.Handle("/api/v2/metric/alerts", handler.New( + provider.authZ.ViewAccess(provider.metricsExplorerHandler.GetMetricAlerts), + handler.OpenAPIDef{ + ID: "GetMetricAlerts", + Tags: []string{"metrics"}, + Summary: "Get metric alerts", + Description: "This endpoint returns associated alerts for a specified metric", + Request: nil, + RequestContentType: "", + Response: new(metricsexplorertypes.MetricAlertsResponse), + ResponseContentType: "application/json", + SuccessStatusCode: http.StatusOK, + ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError}, + Deprecated: false, + SecuritySchemes: newSecuritySchemes(types.RoleViewer), + })).Methods(http.MethodGet).GetError(); err != nil { + return err + } + + if err := router.Handle("/api/v2/metric/dashboards", handler.New( + provider.authZ.ViewAccess(provider.metricsExplorerHandler.GetMetricDashboards), + handler.OpenAPIDef{ + ID: "GetMetricDashboards", + Tags: []string{"metrics"}, + Summary: "Get metric dashboards", + Description: "This endpoint returns associated dashboards for a specified metric", + Request: nil, + RequestContentType: "", + Response: new(metricsexplorertypes.MetricDashboardsResponse), + ResponseContentType: "application/json", + SuccessStatusCode: http.StatusOK, + ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError}, + Deprecated: false, + SecuritySchemes: newSecuritySchemes(types.RoleViewer), + })).Methods(http.MethodGet).GetError(); err != nil { + return err + } + + return nil +} diff --git a/pkg/apiserver/signozapiserver/provider.go b/pkg/apiserver/signozapiserver/provider.go index 8396a90262..ca7eb0b197 100644 --- a/pkg/apiserver/signozapiserver/provider.go +++ b/pkg/apiserver/signozapiserver/provider.go @@ -12,6 +12,7 @@ import ( "github.com/SigNoz/signoz/pkg/http/middleware" "github.com/SigNoz/signoz/pkg/modules/authdomain" "github.com/SigNoz/signoz/pkg/modules/dashboard" + "github.com/SigNoz/signoz/pkg/modules/metricsexplorer" "github.com/SigNoz/signoz/pkg/modules/organization" "github.com/SigNoz/signoz/pkg/modules/preference" "github.com/SigNoz/signoz/pkg/modules/promote" @@ -23,20 +24,21 @@ import ( ) type provider struct { - config apiserver.Config - settings factory.ScopedProviderSettings - router *mux.Router - authZ *middleware.AuthZ - orgHandler organization.Handler - userHandler user.Handler - sessionHandler session.Handler - authDomainHandler authdomain.Handler - preferenceHandler preference.Handler - globalHandler global.Handler - promoteHandler promote.Handler - flaggerHandler flagger.Handler - dashboardModule dashboard.Module - dashboardHandler dashboard.Handler + config apiserver.Config + settings factory.ScopedProviderSettings + router *mux.Router + authZ *middleware.AuthZ + orgHandler organization.Handler + userHandler user.Handler + sessionHandler session.Handler + authDomainHandler authdomain.Handler + preferenceHandler preference.Handler + globalHandler global.Handler + promoteHandler promote.Handler + flaggerHandler flagger.Handler + dashboardModule dashboard.Module + dashboardHandler dashboard.Handler + metricsExplorerHandler metricsexplorer.Handler } func NewFactory( @@ -52,9 +54,10 @@ func NewFactory( flaggerHandler flagger.Handler, dashboardModule dashboard.Module, dashboardHandler dashboard.Handler, + metricsExplorerHandler metricsexplorer.Handler, ) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] { return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) { - return newProvider(ctx, providerSettings, config, orgGetter, authz, orgHandler, userHandler, sessionHandler, authDomainHandler, preferenceHandler, globalHandler, promoteHandler, flaggerHandler, dashboardModule, dashboardHandler) + return newProvider(ctx, providerSettings, config, orgGetter, authz, orgHandler, userHandler, sessionHandler, authDomainHandler, preferenceHandler, globalHandler, promoteHandler, flaggerHandler, dashboardModule, dashboardHandler, metricsExplorerHandler) }) } @@ -74,24 +77,26 @@ func newProvider( flaggerHandler flagger.Handler, dashboardModule dashboard.Module, dashboardHandler dashboard.Handler, + metricsExplorerHandler metricsexplorer.Handler, ) (apiserver.APIServer, error) { settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver") router := mux.NewRouter().UseEncodedPath() provider := &provider{ - config: config, - settings: settings, - router: router, - orgHandler: orgHandler, - userHandler: userHandler, - sessionHandler: sessionHandler, - authDomainHandler: authDomainHandler, - preferenceHandler: preferenceHandler, - globalHandler: globalHandler, - promoteHandler: promoteHandler, - flaggerHandler: flaggerHandler, - dashboardModule: dashboardModule, - dashboardHandler: dashboardHandler, + config: config, + settings: settings, + router: router, + orgHandler: orgHandler, + userHandler: userHandler, + sessionHandler: sessionHandler, + authDomainHandler: authDomainHandler, + preferenceHandler: preferenceHandler, + globalHandler: globalHandler, + promoteHandler: promoteHandler, + flaggerHandler: flaggerHandler, + dashboardModule: dashboardModule, + dashboardHandler: dashboardHandler, + metricsExplorerHandler: metricsExplorerHandler, } provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz) @@ -144,6 +149,10 @@ func (provider *provider) AddToRouter(router *mux.Router) error { return err } + if err := provider.addMetricsExplorerV2Routes(router); err != nil { + return err + } + return nil } diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 24d4c82a6c..8312048eca 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -626,15 +626,6 @@ func (ah *APIHandler) MetricExplorerRoutes(router *mux.Router, am *middleware.Au router.HandleFunc("/api/v1/metrics/{metric_name}/metadata", am.ViewAccess(ah.UpdateMetricsMetadata)). Methods(http.MethodPost) - // v2 endpoints - router.HandleFunc("/api/v2/metrics/stats", am.ViewAccess(ah.Signoz.Handlers.MetricsExplorer.GetStats)).Methods(http.MethodPost) - router.HandleFunc("/api/v2/metrics/treemap", am.ViewAccess(ah.Signoz.Handlers.MetricsExplorer.GetTreemap)).Methods(http.MethodPost) - router.HandleFunc("/api/v2/metrics/attributes", am.ViewAccess(ah.Signoz.Handlers.MetricsExplorer.GetMetricAttributes)).Methods(http.MethodPost) - router.HandleFunc("/api/v2/metrics/metadata", am.ViewAccess(ah.Signoz.Handlers.MetricsExplorer.GetMetricMetadata)).Methods(http.MethodGet) - router.HandleFunc("/api/v2/metrics/{metric_name}/metadata", am.EditAccess(ah.Signoz.Handlers.MetricsExplorer.UpdateMetricMetadata)).Methods(http.MethodPost) - router.HandleFunc("/api/v2/metric/highlights", am.ViewAccess(ah.Signoz.Handlers.MetricsExplorer.GetMetricHighlights)).Methods(http.MethodGet) - router.HandleFunc("/api/v2/metric/alerts", am.ViewAccess(ah.Signoz.Handlers.MetricsExplorer.GetMetricAlerts)).Methods(http.MethodGet) - router.HandleFunc("/api/v2/metric/dashboards", am.ViewAccess(ah.Signoz.Handlers.MetricsExplorer.GetMetricDashboards)).Methods(http.MethodGet) } func Intersection(a, b []int) (c []int) { diff --git a/pkg/ruler/signozruler/provider.go b/pkg/ruler/signozruler/provider.go index 396ca4f8cd..471502fdc3 100644 --- a/pkg/ruler/signozruler/provider.go +++ b/pkg/ruler/signozruler/provider.go @@ -16,14 +16,13 @@ type provider struct { ruleStore ruletypes.RuleStore } -func NewFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[ruler.Ruler, ruler.Config] { +func NewFactory(sqlstore sqlstore.SQLStore, queryParser queryparser.QueryParser) factory.ProviderFactory[ruler.Ruler, ruler.Config] { return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, settings factory.ProviderSettings, config ruler.Config) (ruler.Ruler, error) { - return New(ctx, settings, config, sqlstore) + return New(ctx, settings, config, sqlstore, queryParser) }) } -func New(ctx context.Context, settings factory.ProviderSettings, config ruler.Config, sqlstore sqlstore.SQLStore) (ruler.Ruler, error) { - queryParser := queryparser.New(settings) +func New(ctx context.Context, settings factory.ProviderSettings, config ruler.Config, sqlstore sqlstore.SQLStore, queryParser queryparser.QueryParser) (ruler.Ruler, error) { return &provider{ruleStore: sqlrulestore.NewRuleStore(sqlstore, queryParser, settings)}, nil } diff --git a/pkg/signoz/openapi.go b/pkg/signoz/openapi.go index 50dbec33d9..453aea1d32 100644 --- a/pkg/signoz/openapi.go +++ b/pkg/signoz/openapi.go @@ -14,6 +14,7 @@ import ( "github.com/SigNoz/signoz/pkg/instrumentation" "github.com/SigNoz/signoz/pkg/modules/authdomain" "github.com/SigNoz/signoz/pkg/modules/dashboard" + "github.com/SigNoz/signoz/pkg/modules/metricsexplorer" "github.com/SigNoz/signoz/pkg/modules/organization" "github.com/SigNoz/signoz/pkg/modules/preference" "github.com/SigNoz/signoz/pkg/modules/promote" @@ -45,6 +46,7 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta struct{ flagger.Handler }{}, struct{ dashboard.Module }{}, struct{ dashboard.Handler }{}, + struct{ metricsexplorer.Handler }{}, ).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{}) if err != nil { return nil, err diff --git a/pkg/signoz/provider.go b/pkg/signoz/provider.go index 85982c9123..be9d4e484f 100644 --- a/pkg/signoz/provider.go +++ b/pkg/signoz/provider.go @@ -34,6 +34,7 @@ import ( "github.com/SigNoz/signoz/pkg/prometheus/clickhouseprometheus" "github.com/SigNoz/signoz/pkg/querier" "github.com/SigNoz/signoz/pkg/querier/signozquerier" + "github.com/SigNoz/signoz/pkg/queryparser" "github.com/SigNoz/signoz/pkg/ruler" "github.com/SigNoz/signoz/pkg/ruler/signozruler" "github.com/SigNoz/signoz/pkg/sharder" @@ -197,9 +198,9 @@ func NewAlertmanagerProviderFactories(sqlstore sqlstore.SQLStore, orgGetter orga ) } -func NewRulerProviderFactories(sqlstore sqlstore.SQLStore) factory.NamedMap[factory.ProviderFactory[ruler.Ruler, ruler.Config]] { +func NewRulerProviderFactories(sqlstore sqlstore.SQLStore, queryParser queryparser.QueryParser) factory.NamedMap[factory.ProviderFactory[ruler.Ruler, ruler.Config]] { return factory.MustNewNamedMap( - signozruler.NewFactory(sqlstore), + signozruler.NewFactory(sqlstore, queryParser), ) } @@ -245,6 +246,7 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au handlers.FlaggerHandler, modules.Dashboard, handlers.Dashboard, + handlers.MetricsExplorer, ), ) } diff --git a/pkg/signoz/provider_test.go b/pkg/signoz/provider_test.go index 92847f3793..c863cecbd7 100644 --- a/pkg/signoz/provider_test.go +++ b/pkg/signoz/provider_test.go @@ -9,6 +9,7 @@ import ( "github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest" "github.com/SigNoz/signoz/pkg/modules/organization/implorganization" "github.com/SigNoz/signoz/pkg/modules/user/impluser" + "github.com/SigNoz/signoz/pkg/queryparser" "github.com/SigNoz/signoz/pkg/sqlschema" "github.com/SigNoz/signoz/pkg/sqlschema/sqlschematest" "github.com/SigNoz/signoz/pkg/sqlstore" @@ -61,7 +62,8 @@ func TestNewProviderFactories(t *testing.T) { }) assert.NotPanics(t, func() { - NewRulerProviderFactories(sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual)) + queryParser := queryparser.New(instrumentationtest.New().ToProviderSettings()) + NewRulerProviderFactories(sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherEqual), queryParser) }) assert.NotPanics(t, func() { diff --git a/pkg/signoz/signoz.go b/pkg/signoz/signoz.go index 0fcd02dbfa..42a86bf2e1 100644 --- a/pkg/signoz/signoz.go +++ b/pkg/signoz/signoz.go @@ -311,12 +311,15 @@ func New( return nil, err } + // Initialize query parser + queryParser := queryparser.New(providerSettings) + // Initialize ruler from the available ruler provider factories ruler, err := factory.NewProviderFromNamedMap( ctx, providerSettings, config.Ruler, - NewRulerProviderFactories(sqlstore), + NewRulerProviderFactories(sqlstore, queryParser), "signoz", ) if err != nil { @@ -333,9 +336,6 @@ func New( return nil, err } - // Initialize query parser - queryParser := queryparser.New(providerSettings) - // Initialize authns store := sqlauthnstore.NewStore(sqlstore) authNs, err := authNsCallback(ctx, providerSettings, store, licensing)