2021-01-03 18:15:44 +05:30
package app
import (
2022-07-18 16:37:46 +05:30
"bytes"
2021-05-22 19:51:56 +05:30
"context"
2021-01-03 18:15:44 +05:30
"encoding/json"
2022-05-03 15:26:32 +05:30
"errors"
2021-01-03 18:15:44 +05:30
"fmt"
2023-08-11 21:54:44 +05:30
"io"
2024-08-09 12:11:05 +05:30
"math"
2021-01-03 18:15:44 +05:30
"net/http"
2024-03-11 16:45:06 +05:30
"regexp"
2024-03-18 18:20:12 +05:30
"slices"
2024-10-04 01:10:13 +05:30
"sort"
2022-06-24 14:52:11 +05:30
"strconv"
2022-09-11 03:34:02 +05:30
"strings"
2022-06-24 14:52:11 +05:30
"sync"
2022-09-11 03:34:02 +05:30
"text/template"
2022-06-24 14:52:11 +05:30
"time"
2021-01-03 18:15:44 +05:30
"github.com/gorilla/mux"
2024-08-14 19:53:36 +05:30
"github.com/gorilla/websocket"
2021-08-29 10:28:40 +05:30
jsoniter "github.com/json-iterator/go"
2021-09-02 13:18:47 +05:30
_ "github.com/mattn/go-sqlite3"
2021-08-29 10:28:40 +05:30
"github.com/prometheus/prometheus/promql"
2023-10-19 14:16:20 +05:30
2023-07-31 21:34:42 +05:30
"go.signoz.io/signoz/pkg/query-service/agentConf"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/app/dashboards"
2023-03-07 00:26:25 +05:30
"go.signoz.io/signoz/pkg/query-service/app/explorer"
2024-10-10 17:02:46 +05:30
"go.signoz.io/signoz/pkg/query-service/app/inframetrics"
2024-03-02 10:11:51 +05:30
"go.signoz.io/signoz/pkg/query-service/app/integrations"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/app/logs"
2023-04-10 19:36:13 +05:30
logsv3 "go.signoz.io/signoz/pkg/query-service/app/logs/v3"
2024-09-13 17:04:22 +05:30
logsv4 "go.signoz.io/signoz/pkg/query-service/app/logs/v4"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/app/metrics"
2023-03-23 19:45:15 +05:30
metricsv3 "go.signoz.io/signoz/pkg/query-service/app/metrics/v3"
2024-07-29 09:51:18 +05:30
"go.signoz.io/signoz/pkg/query-service/app/preferences"
2023-09-17 10:40:45 +05:30
"go.signoz.io/signoz/pkg/query-service/app/querier"
2024-01-16 16:56:20 +05:30
querierV2 "go.signoz.io/signoz/pkg/query-service/app/querier/v2"
2023-05-09 19:16:55 +05:30
"go.signoz.io/signoz/pkg/query-service/app/queryBuilder"
2023-04-25 21:53:46 +05:30
tracesV3 "go.signoz.io/signoz/pkg/query-service/app/traces/v3"
2024-11-22 12:00:29 +05:30
tracesV4 "go.signoz.io/signoz/pkg/query-service/app/traces/v4"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/auth"
2023-09-17 10:40:45 +05:30
"go.signoz.io/signoz/pkg/query-service/cache"
2024-07-29 09:51:18 +05:30
"go.signoz.io/signoz/pkg/query-service/common"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/constants"
2024-09-17 15:33:17 +05:30
"go.signoz.io/signoz/pkg/query-service/contextlinks"
2023-03-04 00:05:16 +05:30
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
2024-05-24 12:11:34 +05:30
"go.signoz.io/signoz/pkg/query-service/postprocess"
2022-10-06 20:13:30 +05:30
2023-10-19 14:16:20 +05:30
"go.uber.org/zap"
2024-07-26 11:50:02 +05:30
mq "go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
2023-07-31 21:34:42 +05:30
"go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/dao"
am "go.signoz.io/signoz/pkg/query-service/integrations/alertManager"
2022-11-14 14:29:13 +05:30
signozio "go.signoz.io/signoz/pkg/query-service/integrations/signozio"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/interfaces"
"go.signoz.io/signoz/pkg/query-service/model"
"go.signoz.io/signoz/pkg/query-service/rules"
"go.signoz.io/signoz/pkg/query-service/telemetry"
"go.signoz.io/signoz/pkg/query-service/version"
2021-01-03 18:15:44 +05:30
)
2021-08-29 10:28:40 +05:30
type status string
const (
2023-09-17 10:40:45 +05:30
statusSuccess status = "success"
statusError status = "error"
defaultFluxInterval = 5 * time . Minute
2021-08-29 10:28:40 +05:30
)
2021-01-03 18:15:44 +05:30
// NewRouter creates and configures a Gorilla Router.
func NewRouter ( ) * mux . Router {
return mux . NewRouter ( ) . UseEncodedPath ( )
}
2024-06-11 20:10:38 +05:30
// APIHandler implements the query service public API
2021-01-03 18:15:44 +05:30
type APIHandler struct {
2023-07-14 11:31:44 +05:30
reader interfaces . Reader
skipConfig * model . SkipConfig
appDao dao . ModelDao
alertManager am . Manager
ruleManager * rules . Manager
featureFlags interfaces . FeatureLookup
2023-09-17 10:40:45 +05:30
querier interfaces . Querier
2024-01-16 16:56:20 +05:30
querierV2 interfaces . Querier
2023-07-14 11:31:44 +05:30
queryBuilder * queryBuilder . QueryBuilder
preferSpanMetrics bool
2023-02-24 14:57:07 +05:30
2024-02-11 00:31:47 +05:30
// temporalityMap is a map of metric name to temporality
// to avoid fetching temporality for the same metric multiple times
// querying the v4 table on low cardinal temporality column
// should be fast but we can still avoid the query if we have the data in memory
temporalityMap map [ string ] map [ v3 . Temporality ] bool
2024-07-10 11:00:28 +05:30
temporalityMux sync . Mutex
2024-02-11 00:31:47 +05:30
2023-08-10 17:20:34 +05:30
maxIdleConns int
maxOpenConns int
dialTimeout time . Duration
2024-03-02 10:11:51 +05:30
IntegrationsController * integrations . Controller
2023-07-31 21:34:42 +05:30
LogsParsingPipelineController * logparsingpipeline . LogParsingPipelineController
2023-02-24 14:57:07 +05:30
// SetupCompleted indicates if SigNoz is ready for general use.
// at the moment, we mark the app ready when the first user
// is registers.
SetupCompleted bool
2024-08-14 19:53:36 +05:30
// Websocket connection upgrader
Upgrader * websocket . Upgrader
2024-09-12 10:58:07 +05:30
2024-11-22 12:00:29 +05:30
UseLogsNewSchema bool
UseTraceNewSchema bool
2024-10-10 17:02:46 +05:30
2024-10-24 12:17:24 +05:30
hostsRepo * inframetrics . HostsRepo
processesRepo * inframetrics . ProcessesRepo
podsRepo * inframetrics . PodsRepo
nodesRepo * inframetrics . NodesRepo
namespacesRepo * inframetrics . NamespacesRepo
clustersRepo * inframetrics . ClustersRepo
2024-11-12 20:53:40 +05:30
// workloads
deploymentsRepo * inframetrics . DeploymentsRepo
daemonsetsRepo * inframetrics . DaemonSetsRepo
statefulsetsRepo * inframetrics . StatefulSetsRepo
jobsRepo * inframetrics . JobsRepo
2024-12-19 17:31:12 +05:30
pvcsRepo * inframetrics . PvcsRepo
2021-01-03 18:15:44 +05:30
}
2022-10-06 20:13:30 +05:30
type APIHandlerOpts struct {
// business data reader e.g. clickhouse
Reader interfaces . Reader
2023-06-30 06:58:22 +05:30
SkipConfig * model . SkipConfig
2023-07-13 18:50:19 +05:30
2023-07-14 11:31:44 +05:30
PreferSpanMetrics bool
2023-08-10 17:20:34 +05:30
MaxIdleConns int
MaxOpenConns int
DialTimeout time . Duration
2022-10-06 20:13:30 +05:30
// dao layer to perform crud on app objects like dashboard, alerts etc
AppDao dao . ModelDao
// rule manager handles rule crud operations
RuleManager * rules . Manager
// feature flags querier
FeatureFlags interfaces . FeatureLookup
2023-07-31 21:34:42 +05:30
2024-03-02 10:11:51 +05:30
// Integrations
IntegrationsController * integrations . Controller
2023-07-31 21:34:42 +05:30
// Log parsing pipelines
LogsParsingPipelineController * logparsingpipeline . LogParsingPipelineController
2024-03-02 10:11:51 +05:30
2023-09-17 10:40:45 +05:30
// cache
Cache cache . Cache
// Querier Influx Interval
FluxInterval time . Duration
2024-09-12 10:58:07 +05:30
2024-09-13 17:04:22 +05:30
// Use Logs New schema
2024-09-12 10:58:07 +05:30
UseLogsNewSchema bool
2024-11-12 01:40:10 +05:30
2024-11-22 12:00:29 +05:30
UseTraceNewSchema bool
2022-10-06 20:13:30 +05:30
}
2021-01-03 18:15:44 +05:30
// NewAPIHandler returns an APIHandler
2022-10-06 20:13:30 +05:30
func NewAPIHandler ( opts APIHandlerOpts ) ( * APIHandler , error ) {
2021-08-29 10:28:40 +05:30
2024-09-17 11:41:46 +05:30
alertManager , err := am . New ( )
2022-07-14 11:59:06 +05:30
if err != nil {
return nil , err
}
2022-10-06 20:13:30 +05:30
2023-09-17 10:40:45 +05:30
querierOpts := querier . QuerierOptions {
2024-11-22 12:00:29 +05:30
Reader : opts . Reader ,
Cache : opts . Cache ,
KeyGenerator : queryBuilder . NewKeyGenerator ( ) ,
FluxInterval : opts . FluxInterval ,
FeatureLookup : opts . FeatureFlags ,
UseLogsNewSchema : opts . UseLogsNewSchema ,
UseTraceNewSchema : opts . UseTraceNewSchema ,
2023-09-17 10:40:45 +05:30
}
2024-01-16 16:56:20 +05:30
querierOptsV2 := querierV2 . QuerierOptions {
2024-11-22 12:00:29 +05:30
Reader : opts . Reader ,
Cache : opts . Cache ,
KeyGenerator : queryBuilder . NewKeyGenerator ( ) ,
FluxInterval : opts . FluxInterval ,
FeatureLookup : opts . FeatureFlags ,
UseLogsNewSchema : opts . UseLogsNewSchema ,
UseTraceNewSchema : opts . UseTraceNewSchema ,
2024-01-16 16:56:20 +05:30
}
2023-09-17 10:40:45 +05:30
querier := querier . NewQuerier ( querierOpts )
2024-01-16 16:56:20 +05:30
querierv2 := querierV2 . NewQuerier ( querierOptsV2 )
2023-09-17 10:40:45 +05:30
2024-10-10 17:02:46 +05:30
hostsRepo := inframetrics . NewHostsRepo ( opts . Reader , querierv2 )
2024-10-15 23:02:52 +05:30
processesRepo := inframetrics . NewProcessesRepo ( opts . Reader , querierv2 )
2024-10-24 11:33:51 +05:30
podsRepo := inframetrics . NewPodsRepo ( opts . Reader , querierv2 )
2024-10-24 12:17:24 +05:30
nodesRepo := inframetrics . NewNodesRepo ( opts . Reader , querierv2 )
namespacesRepo := inframetrics . NewNamespacesRepo ( opts . Reader , querierv2 )
clustersRepo := inframetrics . NewClustersRepo ( opts . Reader , querierv2 )
2024-11-12 20:53:40 +05:30
deploymentsRepo := inframetrics . NewDeploymentsRepo ( opts . Reader , querierv2 )
daemonsetsRepo := inframetrics . NewDaemonSetsRepo ( opts . Reader , querierv2 )
statefulsetsRepo := inframetrics . NewStatefulSetsRepo ( opts . Reader , querierv2 )
jobsRepo := inframetrics . NewJobsRepo ( opts . Reader , querierv2 )
2024-12-19 17:31:12 +05:30
pvcsRepo := inframetrics . NewPvcsRepo ( opts . Reader , querierv2 )
2024-10-10 17:02:46 +05:30
2021-01-03 18:15:44 +05:30
aH := & APIHandler {
2023-07-31 21:34:42 +05:30
reader : opts . Reader ,
appDao : opts . AppDao ,
skipConfig : opts . SkipConfig ,
preferSpanMetrics : opts . PreferSpanMetrics ,
2024-02-11 00:31:47 +05:30
temporalityMap : make ( map [ string ] map [ v3 . Temporality ] bool ) ,
2023-08-10 17:20:34 +05:30
maxIdleConns : opts . MaxIdleConns ,
maxOpenConns : opts . MaxOpenConns ,
dialTimeout : opts . DialTimeout ,
2023-07-31 21:34:42 +05:30
alertManager : alertManager ,
ruleManager : opts . RuleManager ,
featureFlags : opts . FeatureFlags ,
2024-03-02 10:11:51 +05:30
IntegrationsController : opts . IntegrationsController ,
2023-07-31 21:34:42 +05:30
LogsParsingPipelineController : opts . LogsParsingPipelineController ,
2023-09-17 10:40:45 +05:30
querier : querier ,
2024-01-16 16:56:20 +05:30
querierV2 : querierv2 ,
2024-09-12 10:58:07 +05:30
UseLogsNewSchema : opts . UseLogsNewSchema ,
2024-11-22 12:00:29 +05:30
UseTraceNewSchema : opts . UseTraceNewSchema ,
2024-10-10 17:02:46 +05:30
hostsRepo : hostsRepo ,
2024-10-15 23:02:52 +05:30
processesRepo : processesRepo ,
2024-10-24 11:33:51 +05:30
podsRepo : podsRepo ,
2024-10-24 12:17:24 +05:30
nodesRepo : nodesRepo ,
namespacesRepo : namespacesRepo ,
clustersRepo : clustersRepo ,
2024-11-12 20:53:40 +05:30
deploymentsRepo : deploymentsRepo ,
daemonsetsRepo : daemonsetsRepo ,
statefulsetsRepo : statefulsetsRepo ,
jobsRepo : jobsRepo ,
2024-12-19 17:31:12 +05:30
pvcsRepo : pvcsRepo ,
2021-01-03 18:15:44 +05:30
}
2022-10-06 20:13:30 +05:30
2024-09-13 17:04:22 +05:30
logsQueryBuilder := logsv3 . PrepareLogsQuery
if opts . UseLogsNewSchema {
logsQueryBuilder = logsv4 . PrepareLogsQuery
}
2024-11-22 12:00:29 +05:30
tracesQueryBuilder := tracesV3 . PrepareTracesQuery
if opts . UseTraceNewSchema {
tracesQueryBuilder = tracesV4 . PrepareTracesQuery
}
2023-05-09 19:16:55 +05:30
builderOpts := queryBuilder . QueryBuilderOptions {
2023-03-23 19:45:15 +05:30
BuildMetricQuery : metricsv3 . PrepareMetricQuery ,
2024-11-22 12:00:29 +05:30
BuildTraceQuery : tracesQueryBuilder ,
2024-09-13 17:04:22 +05:30
BuildLogQuery : logsQueryBuilder ,
2023-03-23 19:45:15 +05:30
}
2023-08-18 07:32:05 +05:30
aH . queryBuilder = queryBuilder . NewQueryBuilder ( builderOpts , aH . featureFlags )
2023-03-23 19:45:15 +05:30
2023-05-17 16:10:43 +05:30
dashboards . LoadDashboardFiles ( aH . featureFlags )
2022-01-26 21:40:44 +05:30
// if errReadingDashboards != nil {
// return nil, errReadingDashboards
// }
2023-02-24 14:57:07 +05:30
// check if at least one user is created
hasUsers , err := aH . appDao . GetUsersWithOpts ( context . Background ( ) , 1 )
if err . Error ( ) != "" {
// raise warning but no panic as this is a recoverable condition
2024-03-27 00:07:29 +05:30
zap . L ( ) . Warn ( "unexpected error while fetch user count while initializing base api handler" , zap . Error ( err ) )
2023-02-24 14:57:07 +05:30
}
if len ( hasUsers ) != 0 {
// first user is already created, we can mark the app ready for general use.
// this means, we disable self-registration and expect new users
// to signup signoz through invite link only.
aH . SetupCompleted = true
}
2024-08-14 19:53:36 +05:30
aH . Upgrader = & websocket . Upgrader {
CheckOrigin : func ( r * http . Request ) bool {
2024-08-23 17:37:56 +05:30
return true
2024-08-14 19:53:36 +05:30
} ,
}
2021-09-02 13:18:47 +05:30
return aH , nil
2021-01-03 18:15:44 +05:30
}
2024-08-23 13:07:10 +05:30
// todo(remove): Implemented at render package (go.signoz.io/signoz/pkg/http/render) with the new error structure
2021-01-03 18:15:44 +05:30
type structuredResponse struct {
Data interface { } ` json:"data" `
Total int ` json:"total" `
Limit int ` json:"limit" `
Offset int ` json:"offset" `
Errors [ ] structuredError ` json:"errors" `
}
2024-08-23 13:07:10 +05:30
// todo(remove): Implemented at render package (go.signoz.io/signoz/pkg/http/render) with the new error structure
2021-01-03 18:15:44 +05:30
type structuredError struct {
Code int ` json:"code,omitempty" `
Msg string ` json:"msg" `
2021-08-29 10:28:40 +05:30
}
2024-08-23 13:07:10 +05:30
// todo(remove): Implemented at render package (go.signoz.io/signoz/pkg/http/render) with the new error structure
2023-09-02 20:01:03 +05:30
type ApiResponse struct {
2021-08-29 10:28:40 +05:30
Status status ` json:"status" `
Data interface { } ` json:"data,omitempty" `
ErrorType model . ErrorType ` json:"errorType,omitempty" `
Error string ` json:"error,omitempty" `
}
2024-08-23 13:07:10 +05:30
// todo(remove): Implemented at render package (go.signoz.io/signoz/pkg/http/render) with the new error structure
2022-10-06 20:13:30 +05:30
func RespondError ( w http . ResponseWriter , apiErr model . BaseApiError , data interface { } ) {
2021-08-29 10:28:40 +05:30
json := jsoniter . ConfigCompatibleWithStandardLibrary
2023-09-02 20:01:03 +05:30
b , err := json . Marshal ( & ApiResponse {
2021-08-29 10:28:40 +05:30
Status : statusError ,
2022-10-06 20:13:30 +05:30
ErrorType : apiErr . Type ( ) ,
Error : apiErr . Error ( ) ,
2021-08-29 10:28:40 +05:30
Data : data ,
} )
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error marshalling json response" , zap . Error ( err ) )
2021-08-29 10:28:40 +05:30
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
var code int
2022-10-06 20:13:30 +05:30
switch apiErr . Type ( ) {
2021-08-29 10:28:40 +05:30
case model . ErrorBadData :
code = http . StatusBadRequest
case model . ErrorExec :
code = 422
case model . ErrorCanceled , model . ErrorTimeout :
code = http . StatusServiceUnavailable
case model . ErrorInternal :
code = http . StatusInternalServerError
case model . ErrorNotFound :
code = http . StatusNotFound
case model . ErrorNotImplemented :
code = http . StatusNotImplemented
2022-05-03 15:26:32 +05:30
case model . ErrorUnauthorized :
code = http . StatusUnauthorized
2022-05-04 14:50:15 +05:30
case model . ErrorForbidden :
code = http . StatusForbidden
2024-11-13 00:25:00 +05:30
case model . ErrorConflict :
code = http . StatusConflict
2021-08-29 10:28:40 +05:30
default :
code = http . StatusInternalServerError
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( code )
if n , err := w . Write ( b ) ; err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error writing response" , zap . Int ( "bytesWritten" , n ) , zap . Error ( err ) )
2021-08-29 10:28:40 +05:30
}
}
2024-08-23 13:07:10 +05:30
// todo(remove): Implemented at render package (go.signoz.io/signoz/pkg/http/render) with the new error structure
2022-05-03 15:26:32 +05:30
func writeHttpResponse ( w http . ResponseWriter , data interface { } ) {
2021-08-29 10:28:40 +05:30
json := jsoniter . ConfigCompatibleWithStandardLibrary
2023-09-02 20:01:03 +05:30
b , err := json . Marshal ( & ApiResponse {
2021-08-29 10:28:40 +05:30
Status : statusSuccess ,
Data : data ,
} )
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error marshalling json response" , zap . Error ( err ) )
2021-08-29 10:28:40 +05:30
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( http . StatusOK )
if n , err := w . Write ( b ) ; err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error writing response" , zap . Int ( "bytesWritten" , n ) , zap . Error ( err ) )
2021-08-29 10:28:40 +05:30
}
}
2022-10-06 20:13:30 +05:30
2023-03-04 00:05:16 +05:30
func ( aH * APIHandler ) RegisterQueryRangeV3Routes ( router * mux . Router , am * AuthMiddleware ) {
subRouter := router . PathPrefix ( "/api/v3" ) . Subrouter ( )
2023-04-05 12:36:38 +05:30
subRouter . HandleFunc ( "/autocomplete/aggregate_attributes" , am . ViewAccess (
withCacheControl ( AutoCompleteCacheControlAge , aH . autocompleteAggregateAttributes ) ) ) . Methods ( http . MethodGet )
subRouter . HandleFunc ( "/autocomplete/attribute_keys" , am . ViewAccess (
withCacheControl ( AutoCompleteCacheControlAge , aH . autoCompleteAttributeKeys ) ) ) . Methods ( http . MethodGet )
subRouter . HandleFunc ( "/autocomplete/attribute_values" , am . ViewAccess (
withCacheControl ( AutoCompleteCacheControlAge , aH . autoCompleteAttributeValues ) ) ) . Methods ( http . MethodGet )
2023-03-23 19:45:15 +05:30
subRouter . HandleFunc ( "/query_range" , am . ViewAccess ( aH . QueryRangeV3 ) ) . Methods ( http . MethodPost )
2023-12-11 17:46:08 +05:30
subRouter . HandleFunc ( "/query_range/format" , am . ViewAccess ( aH . QueryRangeV3Format ) ) . Methods ( http . MethodPost )
2023-07-20 17:53:55 +05:30
2024-08-08 09:27:41 +05:30
subRouter . HandleFunc ( "/filter_suggestions" , am . ViewAccess ( aH . getQueryBuilderSuggestions ) ) . Methods ( http . MethodGet )
2024-08-20 13:26:34 +05:30
// TODO(Raj): Remove this handler after /ws based path has been completely rolled out.
2024-08-14 19:53:36 +05:30
subRouter . HandleFunc ( "/query_progress" , am . ViewAccess ( aH . GetQueryProgressUpdates ) ) . Methods ( http . MethodGet )
2023-07-20 17:53:55 +05:30
// live logs
2023-08-11 21:54:44 +05:30
subRouter . HandleFunc ( "/logs/livetail" , am . ViewAccess ( aH . liveTailLogs ) ) . Methods ( http . MethodGet )
2023-03-04 00:05:16 +05:30
}
2024-10-10 17:02:46 +05:30
func ( aH * APIHandler ) RegisterInfraMetricsRoutes ( router * mux . Router , am * AuthMiddleware ) {
2024-10-15 23:02:52 +05:30
hostsSubRouter := router . PathPrefix ( "/api/v1/hosts" ) . Subrouter ( )
hostsSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getHostAttributeKeys ) ) . Methods ( http . MethodGet )
hostsSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getHostAttributeValues ) ) . Methods ( http . MethodGet )
hostsSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getHostList ) ) . Methods ( http . MethodPost )
processesSubRouter := router . PathPrefix ( "/api/v1/processes" ) . Subrouter ( )
processesSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getProcessAttributeKeys ) ) . Methods ( http . MethodGet )
processesSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getProcessAttributeValues ) ) . Methods ( http . MethodGet )
processesSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getProcessList ) ) . Methods ( http . MethodPost )
2024-10-24 11:33:51 +05:30
podsSubRouter := router . PathPrefix ( "/api/v1/pods" ) . Subrouter ( )
podsSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getPodAttributeKeys ) ) . Methods ( http . MethodGet )
podsSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getPodAttributeValues ) ) . Methods ( http . MethodGet )
podsSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getPodList ) ) . Methods ( http . MethodPost )
2024-10-24 12:17:24 +05:30
2024-12-19 17:31:12 +05:30
pvcsSubRouter := router . PathPrefix ( "/api/v1/pvcs" ) . Subrouter ( )
pvcsSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getPvcAttributeKeys ) ) . Methods ( http . MethodGet )
pvcsSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getPvcAttributeValues ) ) . Methods ( http . MethodGet )
pvcsSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getPvcList ) ) . Methods ( http . MethodPost )
2024-10-24 12:17:24 +05:30
nodesSubRouter := router . PathPrefix ( "/api/v1/nodes" ) . Subrouter ( )
nodesSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getNodeAttributeKeys ) ) . Methods ( http . MethodGet )
nodesSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getNodeAttributeValues ) ) . Methods ( http . MethodGet )
nodesSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getNodeList ) ) . Methods ( http . MethodPost )
namespacesSubRouter := router . PathPrefix ( "/api/v1/namespaces" ) . Subrouter ( )
namespacesSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getNamespaceAttributeKeys ) ) . Methods ( http . MethodGet )
namespacesSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getNamespaceAttributeValues ) ) . Methods ( http . MethodGet )
namespacesSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getNamespaceList ) ) . Methods ( http . MethodPost )
clustersSubRouter := router . PathPrefix ( "/api/v1/clusters" ) . Subrouter ( )
clustersSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getClusterAttributeKeys ) ) . Methods ( http . MethodGet )
clustersSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getClusterAttributeValues ) ) . Methods ( http . MethodGet )
clustersSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getClusterList ) ) . Methods ( http . MethodPost )
2024-11-12 20:53:40 +05:30
deploymentsSubRouter := router . PathPrefix ( "/api/v1/deployments" ) . Subrouter ( )
deploymentsSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getDeploymentAttributeKeys ) ) . Methods ( http . MethodGet )
deploymentsSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getDeploymentAttributeValues ) ) . Methods ( http . MethodGet )
deploymentsSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getDeploymentList ) ) . Methods ( http . MethodPost )
daemonsetsSubRouter := router . PathPrefix ( "/api/v1/daemonsets" ) . Subrouter ( )
daemonsetsSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getDaemonSetAttributeKeys ) ) . Methods ( http . MethodGet )
daemonsetsSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getDaemonSetAttributeValues ) ) . Methods ( http . MethodGet )
daemonsetsSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getDaemonSetList ) ) . Methods ( http . MethodPost )
statefulsetsSubRouter := router . PathPrefix ( "/api/v1/statefulsets" ) . Subrouter ( )
statefulsetsSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getStatefulSetAttributeKeys ) ) . Methods ( http . MethodGet )
statefulsetsSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getStatefulSetAttributeValues ) ) . Methods ( http . MethodGet )
statefulsetsSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getStatefulSetList ) ) . Methods ( http . MethodPost )
jobsSubRouter := router . PathPrefix ( "/api/v1/jobs" ) . Subrouter ( )
jobsSubRouter . HandleFunc ( "/attribute_keys" , am . ViewAccess ( aH . getJobAttributeKeys ) ) . Methods ( http . MethodGet )
jobsSubRouter . HandleFunc ( "/attribute_values" , am . ViewAccess ( aH . getJobAttributeValues ) ) . Methods ( http . MethodGet )
jobsSubRouter . HandleFunc ( "/list" , am . ViewAccess ( aH . getJobList ) ) . Methods ( http . MethodPost )
2024-10-10 17:02:46 +05:30
}
2024-08-20 13:26:34 +05:30
func ( aH * APIHandler ) RegisterWebSocketPaths ( router * mux . Router , am * AuthMiddleware ) {
subRouter := router . PathPrefix ( "/ws" ) . Subrouter ( )
subRouter . HandleFunc ( "/query_progress" , am . ViewAccess ( aH . GetQueryProgressUpdates ) ) . Methods ( http . MethodGet )
}
2024-01-16 16:56:20 +05:30
func ( aH * APIHandler ) RegisterQueryRangeV4Routes ( router * mux . Router , am * AuthMiddleware ) {
subRouter := router . PathPrefix ( "/api/v4" ) . Subrouter ( )
subRouter . HandleFunc ( "/query_range" , am . ViewAccess ( aH . QueryRangeV4 ) ) . Methods ( http . MethodPost )
2024-02-11 00:31:47 +05:30
subRouter . HandleFunc ( "/metric/metric_metadata" , am . ViewAccess ( aH . getMetricMetadata ) ) . Methods ( http . MethodGet )
2024-01-16 16:56:20 +05:30
}
2024-08-23 13:07:10 +05:30
// todo(remove): Implemented at render package (go.signoz.io/signoz/pkg/http/render) with the new error structure
2022-10-06 20:13:30 +05:30
func ( aH * APIHandler ) Respond ( w http . ResponseWriter , data interface { } ) {
2022-05-03 15:26:32 +05:30
writeHttpResponse ( w , data )
}
2022-06-08 12:22:25 +05:30
// RegisterPrivateRoutes registers routes for this handler on the given router
func ( aH * APIHandler ) RegisterPrivateRoutes ( router * mux . Router ) {
router . HandleFunc ( "/api/v1/channels" , aH . listChannels ) . Methods ( http . MethodGet )
}
2021-01-03 18:15:44 +05:30
// RegisterRoutes registers routes for this handler on the given router
2023-02-15 23:49:03 +05:30
func ( aH * APIHandler ) RegisterRoutes ( router * mux . Router , am * AuthMiddleware ) {
router . HandleFunc ( "/api/v1/query_range" , am . ViewAccess ( aH . queryRangeMetrics ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/query" , am . ViewAccess ( aH . queryMetrics ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/channels" , am . ViewAccess ( aH . listChannels ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/channels/{id}" , am . ViewAccess ( aH . getChannel ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/channels/{id}" , am . AdminAccess ( aH . editChannel ) ) . Methods ( http . MethodPut )
router . HandleFunc ( "/api/v1/channels/{id}" , am . AdminAccess ( aH . deleteChannel ) ) . Methods ( http . MethodDelete )
router . HandleFunc ( "/api/v1/channels" , am . EditAccess ( aH . createChannel ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/testChannel" , am . EditAccess ( aH . testChannel ) ) . Methods ( http . MethodPost )
2023-08-24 17:14:42 +05:30
router . HandleFunc ( "/api/v1/alerts" , am . ViewAccess ( aH . getAlerts ) ) . Methods ( http . MethodGet )
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/rules" , am . ViewAccess ( aH . listRules ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/rules/{id}" , am . ViewAccess ( aH . getRule ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/rules" , am . EditAccess ( aH . createRule ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/rules/{id}" , am . EditAccess ( aH . editRule ) ) . Methods ( http . MethodPut )
router . HandleFunc ( "/api/v1/rules/{id}" , am . EditAccess ( aH . deleteRule ) ) . Methods ( http . MethodDelete )
router . HandleFunc ( "/api/v1/rules/{id}" , am . EditAccess ( aH . patchRule ) ) . Methods ( http . MethodPatch )
router . HandleFunc ( "/api/v1/testRule" , am . EditAccess ( aH . testRule ) ) . Methods ( http . MethodPost )
2024-08-09 12:11:05 +05:30
router . HandleFunc ( "/api/v1/rules/{id}/history/stats" , am . ViewAccess ( aH . getRuleStats ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/rules/{id}/history/timeline" , am . ViewAccess ( aH . getRuleStateHistory ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/rules/{id}/history/top_contributors" , am . ViewAccess ( aH . getRuleStateHistoryTopContributors ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/rules/{id}/history/overall_status" , am . ViewAccess ( aH . getOverallStateTransitions ) ) . Methods ( http . MethodPost )
2023-02-15 23:49:03 +05:30
2024-10-24 13:07:03 +05:30
router . HandleFunc ( "/api/v1/downtime_schedules" , am . ViewAccess ( aH . listDowntimeSchedules ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/downtime_schedules/{id}" , am . ViewAccess ( aH . getDowntimeSchedule ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/downtime_schedules" , am . EditAccess ( aH . createDowntimeSchedule ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/downtime_schedules/{id}" , am . EditAccess ( aH . editDowntimeSchedule ) ) . Methods ( http . MethodPut )
router . HandleFunc ( "/api/v1/downtime_schedules/{id}" , am . EditAccess ( aH . deleteDowntimeSchedule ) ) . Methods ( http . MethodDelete )
2024-05-31 17:43:13 +05:30
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/dashboards" , am . ViewAccess ( aH . getDashboards ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/dashboards" , am . EditAccess ( aH . createDashboards ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/dashboards/{uuid}" , am . ViewAccess ( aH . getDashboard ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/dashboards/{uuid}" , am . EditAccess ( aH . updateDashboard ) ) . Methods ( http . MethodPut )
router . HandleFunc ( "/api/v1/dashboards/{uuid}" , am . EditAccess ( aH . deleteDashboard ) ) . Methods ( http . MethodDelete )
router . HandleFunc ( "/api/v2/variables/query" , am . ViewAccess ( aH . queryDashboardVarsV2 ) ) . Methods ( http . MethodPost )
2023-08-25 09:22:46 +05:30
router . HandleFunc ( "/api/v1/explorer/views" , am . ViewAccess ( aH . getSavedViews ) ) . Methods ( http . MethodGet )
2024-02-13 21:50:52 +05:30
router . HandleFunc ( "/api/v1/explorer/views" , am . EditAccess ( aH . createSavedViews ) ) . Methods ( http . MethodPost )
2023-08-25 09:22:46 +05:30
router . HandleFunc ( "/api/v1/explorer/views/{viewId}" , am . ViewAccess ( aH . getSavedView ) ) . Methods ( http . MethodGet )
2024-02-13 21:50:52 +05:30
router . HandleFunc ( "/api/v1/explorer/views/{viewId}" , am . EditAccess ( aH . updateSavedView ) ) . Methods ( http . MethodPut )
router . HandleFunc ( "/api/v1/explorer/views/{viewId}" , am . EditAccess ( aH . deleteSavedView ) ) . Methods ( http . MethodDelete )
2023-03-07 00:26:25 +05:30
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/feedback" , am . OpenAccess ( aH . submitFeedback ) ) . Methods ( http . MethodPost )
2024-04-01 15:06:38 +05:30
router . HandleFunc ( "/api/v1/event" , am . ViewAccess ( aH . registerEvent ) ) . Methods ( http . MethodPost )
2024-03-28 21:43:41 +05:30
2021-05-27 12:52:34 +05:30
// router.HandleFunc("/api/v1/get_percentiles", aH.getApplicationPercentiles).Methods(http.MethodGet)
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/services" , am . ViewAccess ( aH . getServices ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/services/list" , am . ViewAccess ( aH . getServicesList ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/service/top_operations" , am . ViewAccess ( aH . getTopOperations ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/service/top_level_operations" , am . ViewAccess ( aH . getServicesTopLevelOps ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/traces/{traceId}" , am . ViewAccess ( aH . SearchTraces ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/usage" , am . ViewAccess ( aH . getUsage ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/dependency_graph" , am . ViewAccess ( aH . dependencyGraph ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/settings/ttl" , am . AdminAccess ( aH . setTTL ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/settings/ttl" , am . ViewAccess ( aH . getTTL ) ) . Methods ( http . MethodGet )
2023-07-26 12:27:46 +05:30
router . HandleFunc ( "/api/v1/settings/apdex" , am . AdminAccess ( aH . setApdexSettings ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/settings/apdex" , am . ViewAccess ( aH . getApdexSettings ) ) . Methods ( http . MethodGet )
2023-10-09 21:06:01 +05:30
router . HandleFunc ( "/api/v1/settings/ingestion_key" , am . AdminAccess ( aH . insertIngestionKey ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/settings/ingestion_key" , am . ViewAccess ( aH . getIngestionKeys ) ) . Methods ( http . MethodGet )
2023-07-26 12:27:46 +05:30
2024-12-19 11:52:20 +07:00
router . HandleFunc ( "/api/v2/traces/fields" , am . ViewAccess ( aH . traceFields ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v2/traces/fields" , am . EditAccess ( aH . updateTraceField ) ) . Methods ( http . MethodPost )
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/version" , am . OpenAccess ( aH . getVersion ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/featureFlags" , am . OpenAccess ( aH . getFeatureFlags ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/configs" , am . OpenAccess ( aH . getConfigs ) ) . Methods ( http . MethodGet )
2023-03-03 18:07:24 +05:30
router . HandleFunc ( "/api/v1/health" , am . OpenAccess ( aH . getHealth ) ) . Methods ( http . MethodGet )
2023-02-15 23:49:03 +05:30
2023-03-28 00:15:15 +05:30
router . HandleFunc ( "/api/v1/listErrors" , am . ViewAccess ( aH . listErrors ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/countErrors" , am . ViewAccess ( aH . countErrors ) ) . Methods ( http . MethodPost )
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/errorFromErrorID" , am . ViewAccess ( aH . getErrorFromErrorID ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/errorFromGroupID" , am . ViewAccess ( aH . getErrorFromGroupID ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/nextPrevErrorIDs" , am . ViewAccess ( aH . getNextPrevErrorIDs ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/disks" , am . ViewAccess ( aH . getDisks ) ) . Methods ( http . MethodGet )
2022-05-03 15:26:32 +05:30
2024-07-31 16:00:57 +05:30
// === Preference APIs ===
2024-07-29 09:51:18 +05:30
// user actions
router . HandleFunc ( "/api/v1/user/preferences" , am . ViewAccess ( aH . getAllUserPreferences ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/user/preferences/{preferenceId}" , am . ViewAccess ( aH . getUserPreference ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/user/preferences/{preferenceId}" , am . ViewAccess ( aH . updateUserPreference ) ) . Methods ( http . MethodPut )
// org actions
router . HandleFunc ( "/api/v1/org/preferences" , am . AdminAccess ( aH . getAllOrgPreferences ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/org/preferences/{preferenceId}" , am . AdminAccess ( aH . getOrgPreference ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/org/preferences/{preferenceId}" , am . AdminAccess ( aH . updateOrgPreference ) ) . Methods ( http . MethodPut )
2022-05-03 15:26:32 +05:30
// === Authentication APIs ===
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/invite" , am . AdminAccess ( aH . inviteUser ) ) . Methods ( http . MethodPost )
2024-10-17 18:41:31 +05:30
router . HandleFunc ( "/api/v1/invite/bulk" , am . AdminAccess ( aH . inviteUsers ) ) . Methods ( http . MethodPost )
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/invite/{token}" , am . OpenAccess ( aH . getInvite ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/invite/{email}" , am . AdminAccess ( aH . revokeInvite ) ) . Methods ( http . MethodDelete )
router . HandleFunc ( "/api/v1/invite" , am . AdminAccess ( aH . listPendingInvites ) ) . Methods ( http . MethodGet )
2022-05-03 15:26:32 +05:30
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/register" , am . OpenAccess ( aH . registerUser ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/login" , am . OpenAccess ( aH . loginUser ) ) . Methods ( http . MethodPost )
2023-09-12 12:53:46 +05:30
router . HandleFunc ( "/api/v1/loginPrecheck" , am . OpenAccess ( aH . precheckLogin ) ) . Methods ( http . MethodGet )
2022-05-03 15:26:32 +05:30
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/user" , am . AdminAccess ( aH . listUsers ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/user/{id}" , am . SelfAccess ( aH . getUser ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/user/{id}" , am . SelfAccess ( aH . editUser ) ) . Methods ( http . MethodPut )
router . HandleFunc ( "/api/v1/user/{id}" , am . AdminAccess ( aH . deleteUser ) ) . Methods ( http . MethodDelete )
2022-05-03 15:26:32 +05:30
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/user/{id}/flags" , am . SelfAccess ( aH . patchUserFlag ) ) . Methods ( http . MethodPatch )
2022-12-09 20:16:09 +05:30
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/rbac/role/{id}" , am . SelfAccess ( aH . getRole ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/rbac/role/{id}" , am . AdminAccess ( aH . editRole ) ) . Methods ( http . MethodPut )
2022-05-03 15:26:32 +05:30
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/org" , am . AdminAccess ( aH . getOrgs ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/org/{id}" , am . AdminAccess ( aH . getOrg ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/org/{id}" , am . AdminAccess ( aH . editOrg ) ) . Methods ( http . MethodPut )
router . HandleFunc ( "/api/v1/orgUsers/{id}" , am . AdminAccess ( aH . getOrgUsers ) ) . Methods ( http . MethodGet )
2022-05-03 15:26:32 +05:30
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/getResetPasswordToken/{id}" , am . AdminAccess ( aH . getResetPasswordToken ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/resetPassword" , am . OpenAccess ( aH . resetPassword ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/changePassword/{id}" , am . SelfAccess ( aH . changePassword ) ) . Methods ( http . MethodPost )
2021-01-03 18:15:44 +05:30
}
2021-09-02 13:18:47 +05:30
func Intersection ( a , b [ ] int ) ( c [ ] int ) {
m := make ( map [ int ] bool )
for _ , item := range a {
m [ item ] = true
}
for _ , item := range b {
if _ , ok := m [ item ] ; ok {
c = append ( c , item )
}
}
return
}
2021-11-22 16:15:58 +05:30
func ( aH * APIHandler ) getRule ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
2023-10-17 17:50:54 +00:00
ruleResponse , err := aH . ruleManager . GetRule ( r . Context ( ) , id )
2022-07-14 11:59:06 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , ruleResponse )
2021-11-22 16:15:58 +05:30
}
2024-05-21 12:01:21 +05:30
// populateTemporality adds the temporality to the query if it is not present
2024-09-24 10:22:52 +05:30
func ( aH * APIHandler ) PopulateTemporality ( ctx context . Context , qp * v3 . QueryRangeParamsV3 ) error {
2024-02-11 00:31:47 +05:30
2024-07-10 11:00:28 +05:30
aH . temporalityMux . Lock ( )
defer aH . temporalityMux . Unlock ( )
2024-02-11 00:31:47 +05:30
missingTemporality := make ( [ ] string , 0 )
metricNameToTemporality := make ( map [ string ] map [ v3 . Temporality ] bool )
if qp . CompositeQuery != nil && len ( qp . CompositeQuery . BuilderQueries ) > 0 {
for _ , query := range qp . CompositeQuery . BuilderQueries {
// if there is no temporality specified in the query but we have it in the map
// then use the value from the map
if query . Temporality == "" && aH . temporalityMap [ query . AggregateAttribute . Key ] != nil {
// We prefer delta if it is available
if aH . temporalityMap [ query . AggregateAttribute . Key ] [ v3 . Delta ] {
query . Temporality = v3 . Delta
} else if aH . temporalityMap [ query . AggregateAttribute . Key ] [ v3 . Cumulative ] {
query . Temporality = v3 . Cumulative
} else {
query . Temporality = v3 . Unspecified
}
}
// we don't have temporality for this metric
if query . DataSource == v3 . DataSourceMetrics && query . Temporality == "" {
missingTemporality = append ( missingTemporality , query . AggregateAttribute . Key )
}
if _ , ok := metricNameToTemporality [ query . AggregateAttribute . Key ] ; ! ok {
metricNameToTemporality [ query . AggregateAttribute . Key ] = make ( map [ v3 . Temporality ] bool )
}
}
}
nameToTemporality , err := aH . reader . FetchTemporality ( ctx , missingTemporality )
if err != nil {
return err
}
if qp . CompositeQuery != nil && len ( qp . CompositeQuery . BuilderQueries ) > 0 {
for name := range qp . CompositeQuery . BuilderQueries {
query := qp . CompositeQuery . BuilderQueries [ name ]
if query . DataSource == v3 . DataSourceMetrics && query . Temporality == "" {
if nameToTemporality [ query . AggregateAttribute . Key ] [ v3 . Delta ] {
query . Temporality = v3 . Delta
} else if nameToTemporality [ query . AggregateAttribute . Key ] [ v3 . Cumulative ] {
query . Temporality = v3 . Cumulative
} else {
query . Temporality = v3 . Unspecified
}
aH . temporalityMap [ query . AggregateAttribute . Key ] = nameToTemporality [ query . AggregateAttribute . Key ]
}
}
}
return nil
}
2024-05-31 17:43:13 +05:30
func ( aH * APIHandler ) listDowntimeSchedules ( w http . ResponseWriter , r * http . Request ) {
schedules , err := aH . ruleManager . RuleDB ( ) . GetAllPlannedMaintenance ( r . Context ( ) )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
// The schedules are stored as JSON in the database, so we need to filter them here
// Since the number of schedules is expected to be small, this should be fine
if r . URL . Query ( ) . Get ( "active" ) != "" {
activeSchedules := make ( [ ] rules . PlannedMaintenance , 0 )
active , _ := strconv . ParseBool ( r . URL . Query ( ) . Get ( "active" ) )
for _ , schedule := range schedules {
now := time . Now ( ) . In ( time . FixedZone ( schedule . Schedule . Timezone , 0 ) )
if schedule . IsActive ( now ) == active {
activeSchedules = append ( activeSchedules , schedule )
}
}
schedules = activeSchedules
}
if r . URL . Query ( ) . Get ( "recurring" ) != "" {
recurringSchedules := make ( [ ] rules . PlannedMaintenance , 0 )
recurring , _ := strconv . ParseBool ( r . URL . Query ( ) . Get ( "recurring" ) )
for _ , schedule := range schedules {
if schedule . IsRecurring ( ) == recurring {
recurringSchedules = append ( recurringSchedules , schedule )
}
}
schedules = recurringSchedules
}
aH . Respond ( w , schedules )
}
func ( aH * APIHandler ) getDowntimeSchedule ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
schedule , err := aH . ruleManager . RuleDB ( ) . GetPlannedMaintenanceByID ( r . Context ( ) , id )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , schedule )
}
func ( aH * APIHandler ) createDowntimeSchedule ( w http . ResponseWriter , r * http . Request ) {
var schedule rules . PlannedMaintenance
err := json . NewDecoder ( r . Body ) . Decode ( & schedule )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
if err := schedule . Validate ( ) ; err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
_ , err = aH . ruleManager . RuleDB ( ) . CreatePlannedMaintenance ( r . Context ( ) , schedule )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , nil )
}
func ( aH * APIHandler ) editDowntimeSchedule ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
var schedule rules . PlannedMaintenance
err := json . NewDecoder ( r . Body ) . Decode ( & schedule )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
if err := schedule . Validate ( ) ; err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
_ , err = aH . ruleManager . RuleDB ( ) . EditPlannedMaintenance ( r . Context ( ) , schedule , id )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , nil )
}
func ( aH * APIHandler ) deleteDowntimeSchedule ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
_ , err := aH . ruleManager . RuleDB ( ) . DeletePlannedMaintenance ( r . Context ( ) , id )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , nil )
}
2024-08-09 12:11:05 +05:30
func ( aH * APIHandler ) getRuleStats ( w http . ResponseWriter , r * http . Request ) {
ruleID := mux . Vars ( r ) [ "id" ]
2024-09-13 18:10:49 +05:30
params := model . QueryRuleStateHistory { }
2024-08-09 12:11:05 +05:30
err := json . NewDecoder ( r . Body ) . Decode ( & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
totalCurrentTriggers , err := aH . reader . GetTotalTriggers ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
currentTriggersSeries , err := aH . reader . GetTriggersByInterval ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
currentAvgResolutionTime , err := aH . reader . GetAvgResolutionTime ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
currentAvgResolutionTimeSeries , err := aH . reader . GetAvgResolutionTimeByInterval ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
if params . End - params . Start >= 86400000 {
days := int64 ( math . Ceil ( float64 ( params . End - params . Start ) / 86400000 ) )
params . Start -= days * 86400000
params . End -= days * 86400000
} else {
params . Start -= 86400000
params . End -= 86400000
}
totalPastTriggers , err := aH . reader . GetTotalTriggers ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
pastTriggersSeries , err := aH . reader . GetTriggersByInterval ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
pastAvgResolutionTime , err := aH . reader . GetAvgResolutionTime ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
pastAvgResolutionTimeSeries , err := aH . reader . GetAvgResolutionTimeByInterval ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
2024-08-23 21:13:00 +05:30
if math . IsNaN ( currentAvgResolutionTime ) || math . IsInf ( currentAvgResolutionTime , 0 ) {
currentAvgResolutionTime = 0
}
if math . IsNaN ( pastAvgResolutionTime ) || math . IsInf ( pastAvgResolutionTime , 0 ) {
pastAvgResolutionTime = 0
}
2024-09-13 18:10:49 +05:30
stats := model . Stats {
2024-08-09 12:11:05 +05:30
TotalCurrentTriggers : totalCurrentTriggers ,
TotalPastTriggers : totalPastTriggers ,
CurrentTriggersSeries : currentTriggersSeries ,
PastTriggersSeries : pastTriggersSeries ,
CurrentAvgResolutionTime : strconv . FormatFloat ( currentAvgResolutionTime , 'f' , - 1 , 64 ) ,
PastAvgResolutionTime : strconv . FormatFloat ( pastAvgResolutionTime , 'f' , - 1 , 64 ) ,
CurrentAvgResolutionTimeSeries : currentAvgResolutionTimeSeries ,
PastAvgResolutionTimeSeries : pastAvgResolutionTimeSeries ,
}
aH . Respond ( w , stats )
}
func ( aH * APIHandler ) getOverallStateTransitions ( w http . ResponseWriter , r * http . Request ) {
ruleID := mux . Vars ( r ) [ "id" ]
2024-09-13 18:10:49 +05:30
params := model . QueryRuleStateHistory { }
2024-08-09 12:11:05 +05:30
err := json . NewDecoder ( r . Body ) . Decode ( & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
2024-09-09 13:06:09 +05:30
stateItems , err := aH . reader . GetOverallStateTransitions ( r . Context ( ) , ruleID , & params )
2024-08-09 12:11:05 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , stateItems )
}
2024-09-17 15:33:17 +05:30
func ( aH * APIHandler ) metaForLinks ( ctx context . Context , rule * rules . GettableRule ) ( [ ] v3 . FilterItem , [ ] v3 . AttributeKey , map [ string ] v3 . AttributeKey ) {
filterItems := [ ] v3 . FilterItem { }
groupBy := [ ] v3 . AttributeKey { }
keys := make ( map [ string ] v3 . AttributeKey )
if rule . AlertType == rules . AlertTypeLogs {
logFields , err := aH . reader . GetLogFields ( ctx )
if err == nil {
params := & v3 . QueryRangeParamsV3 {
CompositeQuery : rule . RuleCondition . CompositeQuery ,
}
keys = model . GetLogFieldsV3 ( ctx , params , logFields )
} else {
zap . L ( ) . Error ( "failed to get log fields using empty keys; the link might not work as expected" , zap . Error ( err ) )
}
} else if rule . AlertType == rules . AlertTypeTraces {
traceFields , err := aH . reader . GetSpanAttributeKeys ( ctx )
if err == nil {
keys = traceFields
} else {
zap . L ( ) . Error ( "failed to get span attributes using empty keys; the link might not work as expected" , zap . Error ( err ) )
}
}
if rule . AlertType == rules . AlertTypeLogs || rule . AlertType == rules . AlertTypeTraces {
if rule . RuleCondition . CompositeQuery != nil {
if rule . RuleCondition . QueryType ( ) == v3 . QueryTypeBuilder {
selectedQuery := rule . RuleCondition . GetSelectedQueryName ( )
if rule . RuleCondition . CompositeQuery . BuilderQueries [ selectedQuery ] != nil &&
rule . RuleCondition . CompositeQuery . BuilderQueries [ selectedQuery ] . Filters != nil {
filterItems = rule . RuleCondition . CompositeQuery . BuilderQueries [ selectedQuery ] . Filters . Items
}
if rule . RuleCondition . CompositeQuery . BuilderQueries [ selectedQuery ] != nil &&
rule . RuleCondition . CompositeQuery . BuilderQueries [ selectedQuery ] . GroupBy != nil {
groupBy = rule . RuleCondition . CompositeQuery . BuilderQueries [ selectedQuery ] . GroupBy
}
}
}
}
return filterItems , groupBy , keys
}
2024-08-09 12:11:05 +05:30
func ( aH * APIHandler ) getRuleStateHistory ( w http . ResponseWriter , r * http . Request ) {
ruleID := mux . Vars ( r ) [ "id" ]
2024-09-13 18:10:49 +05:30
params := model . QueryRuleStateHistory { }
2024-08-09 12:11:05 +05:30
err := json . NewDecoder ( r . Body ) . Decode ( & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
if err := params . Validate ( ) ; err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
res , err := aH . reader . ReadRuleStateHistoryByRuleID ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
2024-08-23 21:13:00 +05:30
rule , err := aH . ruleManager . GetRule ( r . Context ( ) , ruleID )
if err == nil {
for idx := range res . Items {
lbls := make ( map [ string ] string )
err := json . Unmarshal ( [ ] byte ( res . Items [ idx ] . Labels ) , & lbls )
if err != nil {
continue
}
2024-09-17 15:33:17 +05:30
filterItems , groupBy , keys := aH . metaForLinks ( r . Context ( ) , rule )
newFilters := contextlinks . PrepareFilters ( lbls , filterItems , groupBy , keys )
end := time . Unix ( res . Items [ idx ] . UnixMilli / 1000 , 0 )
// why are we subtracting 3 minutes?
// the query range is calculated based on the rule's evalWindow and evalDelay
// alerts have 2 minutes delay built in, so we need to subtract that from the start time
// to get the correct query range
start := end . Add ( - time . Duration ( rule . EvalWindow ) ) . Add ( - 3 * time . Minute )
2024-08-30 10:34:11 +05:30
if rule . AlertType == rules . AlertTypeLogs {
2024-09-17 15:33:17 +05:30
res . Items [ idx ] . RelatedLogsLink = contextlinks . PrepareLinksToLogs ( start , end , newFilters )
2024-08-30 10:34:11 +05:30
} else if rule . AlertType == rules . AlertTypeTraces {
2024-09-17 15:33:17 +05:30
res . Items [ idx ] . RelatedTracesLink = contextlinks . PrepareLinksToTraces ( start , end , newFilters )
2024-08-23 21:13:00 +05:30
}
}
}
2024-08-09 12:11:05 +05:30
aH . Respond ( w , res )
}
func ( aH * APIHandler ) getRuleStateHistoryTopContributors ( w http . ResponseWriter , r * http . Request ) {
ruleID := mux . Vars ( r ) [ "id" ]
2024-09-13 18:10:49 +05:30
params := model . QueryRuleStateHistory { }
2024-08-09 12:11:05 +05:30
err := json . NewDecoder ( r . Body ) . Decode ( & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
res , err := aH . reader . ReadRuleStateHistoryTopContributorsByRuleID ( r . Context ( ) , ruleID , & params )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
2024-08-23 21:13:00 +05:30
rule , err := aH . ruleManager . GetRule ( r . Context ( ) , ruleID )
if err == nil {
for idx := range res {
lbls := make ( map [ string ] string )
err := json . Unmarshal ( [ ] byte ( res [ idx ] . Labels ) , & lbls )
if err != nil {
continue
}
2024-09-17 15:33:17 +05:30
filterItems , groupBy , keys := aH . metaForLinks ( r . Context ( ) , rule )
newFilters := contextlinks . PrepareFilters ( lbls , filterItems , groupBy , keys )
end := time . Unix ( params . End / 1000 , 0 )
start := time . Unix ( params . Start / 1000 , 0 )
2024-08-30 10:34:11 +05:30
if rule . AlertType == rules . AlertTypeLogs {
2024-09-17 15:33:17 +05:30
res [ idx ] . RelatedLogsLink = contextlinks . PrepareLinksToLogs ( start , end , newFilters )
2024-08-30 10:34:11 +05:30
} else if rule . AlertType == rules . AlertTypeTraces {
2024-09-17 15:33:17 +05:30
res [ idx ] . RelatedTracesLink = contextlinks . PrepareLinksToTraces ( start , end , newFilters )
2024-08-23 21:13:00 +05:30
}
}
}
2024-08-09 12:11:05 +05:30
aH . Respond ( w , res )
}
2022-07-14 11:59:06 +05:30
func ( aH * APIHandler ) listRules ( w http . ResponseWriter , r * http . Request ) {
2023-10-17 17:50:54 +00:00
rules , err := aH . ruleManager . ListRuleStates ( r . Context ( ) )
2022-07-14 11:59:06 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-07-14 11:59:06 +05:30
// todo(amol): need to add sorter
2022-10-06 20:13:30 +05:30
aH . Respond ( w , rules )
2021-11-22 16:15:58 +05:30
}
2021-09-02 13:18:47 +05:30
func ( aH * APIHandler ) getDashboards ( w http . ResponseWriter , r * http . Request ) {
2023-10-17 17:50:54 +00:00
allDashboards , err := dashboards . GetDashboards ( r . Context ( ) )
2021-09-02 13:18:47 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , err , nil )
2021-09-02 13:18:47 +05:30
return
}
2024-03-11 20:06:59 +05:30
ic := aH . IntegrationsController
installedIntegrationDashboards , err := ic . GetDashboardsForInstalledIntegrations ( r . Context ( ) )
2024-06-11 20:10:38 +05:30
if err != nil {
zap . L ( ) . Error ( "failed to get dashboards for installed integrations" , zap . Error ( err ) )
}
2024-03-11 20:06:59 +05:30
allDashboards = append ( allDashboards , installedIntegrationDashboards ... )
2021-09-02 13:18:47 +05:30
tagsFromReq , ok := r . URL . Query ( ) [ "tags" ]
if ! ok || len ( tagsFromReq ) == 0 || tagsFromReq [ 0 ] == "" {
2022-10-06 20:13:30 +05:30
aH . Respond ( w , allDashboards )
2021-09-02 13:18:47 +05:30
return
}
tags2Dash := make ( map [ string ] [ ] int )
2022-05-03 15:26:32 +05:30
for i := 0 ; i < len ( allDashboards ) ; i ++ {
tags , ok := ( allDashboards ) [ i ] . Data [ "tags" ] . ( [ ] interface { } )
2021-09-02 13:18:47 +05:30
if ! ok {
continue
}
tagsArray := make ( [ ] string , len ( tags ) )
for i , v := range tags {
tagsArray [ i ] = v . ( string )
}
for _ , tag := range tagsArray {
tags2Dash [ tag ] = append ( tags2Dash [ tag ] , i )
}
}
2022-05-03 15:26:32 +05:30
inter := make ( [ ] int , len ( allDashboards ) )
2021-09-02 13:18:47 +05:30
for i := range inter {
inter [ i ] = i
}
for _ , tag := range tagsFromReq {
inter = Intersection ( inter , tags2Dash [ tag ] )
}
filteredDashboards := [ ] dashboards . Dashboard { }
for _ , val := range inter {
2022-05-03 15:26:32 +05:30
dash := ( allDashboards ) [ val ]
2021-09-02 13:18:47 +05:30
filteredDashboards = append ( filteredDashboards , dash )
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , filteredDashboards )
2021-09-02 13:18:47 +05:30
}
func ( aH * APIHandler ) deleteDashboard ( w http . ResponseWriter , r * http . Request ) {
uuid := mux . Vars ( r ) [ "uuid" ]
2023-10-17 17:50:54 +00:00
err := dashboards . DeleteDashboard ( r . Context ( ) , uuid , aH . featureFlags )
2021-09-02 13:18:47 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , err , nil )
2021-09-02 13:18:47 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , nil )
2021-09-02 13:18:47 +05:30
}
2023-01-16 14:57:04 +05:30
func prepareQuery ( r * http . Request ) ( string , error ) {
var postData * model . DashboardVars
if err := json . NewDecoder ( r . Body ) . Decode ( & postData ) ; err != nil {
return "" , fmt . Errorf ( "failed to decode request body: %v" , err )
}
query := strings . TrimSpace ( postData . Query )
if query == "" {
return "" , fmt . Errorf ( "query is required" )
}
notAllowedOps := [ ] string {
"alter table" ,
"drop table" ,
"truncate table" ,
"drop database" ,
"drop view" ,
"drop function" ,
}
for _ , op := range notAllowedOps {
if strings . Contains ( strings . ToLower ( query ) , op ) {
2024-06-11 20:10:38 +05:30
return "" , fmt . Errorf ( "operation %s is not allowed" , op )
2023-01-16 14:57:04 +05:30
}
}
vars := make ( map [ string ] string )
for k , v := range postData . Variables {
vars [ k ] = metrics . FormattedValue ( v )
}
tmpl := template . New ( "dashboard-vars" )
tmpl , tmplErr := tmpl . Parse ( query )
if tmplErr != nil {
return "" , tmplErr
}
var queryBuf bytes . Buffer
tmplErr = tmpl . Execute ( & queryBuf , vars )
if tmplErr != nil {
return "" , tmplErr
}
return queryBuf . String ( ) , nil
}
func ( aH * APIHandler ) queryDashboardVarsV2 ( w http . ResponseWriter , r * http . Request ) {
query , err := prepareQuery ( r )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
dashboardVars , err := aH . reader . QueryDashboardVars ( r . Context ( ) , query )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
aH . Respond ( w , dashboardVars )
}
2021-09-02 13:18:47 +05:30
func ( aH * APIHandler ) updateDashboard ( w http . ResponseWriter , r * http . Request ) {
uuid := mux . Vars ( r ) [ "uuid" ]
var postData map [ string ] interface { }
err := json . NewDecoder ( r . Body ) . Decode ( & postData )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , "Error reading request body" )
2021-09-02 13:18:47 +05:30
return
}
err = dashboards . IsPostDataSane ( & postData )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , "Error reading request body" )
2021-09-02 13:18:47 +05:30
return
}
2023-10-17 17:50:54 +00:00
dashboard , apiError := dashboards . UpdateDashboard ( r . Context ( ) , uuid , postData , aH . featureFlags )
2021-09-02 13:18:47 +05:30
if apiError != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiError , nil )
2021-09-02 13:18:47 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , dashboard )
2021-09-02 13:18:47 +05:30
}
func ( aH * APIHandler ) getDashboard ( w http . ResponseWriter , r * http . Request ) {
uuid := mux . Vars ( r ) [ "uuid" ]
2023-10-17 17:50:54 +00:00
dashboard , apiError := dashboards . GetDashboard ( r . Context ( ) , uuid )
2021-09-02 13:18:47 +05:30
if apiError != nil {
2024-03-11 20:06:59 +05:30
if apiError . Type ( ) != model . ErrorNotFound {
RespondError ( w , apiError , nil )
return
}
dashboard , apiError = aH . IntegrationsController . GetInstalledIntegrationDashboardById (
r . Context ( ) , uuid ,
)
if apiError != nil {
RespondError ( w , apiError , nil )
return
}
2021-09-02 13:18:47 +05:30
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , dashboard )
2021-09-02 13:18:47 +05:30
}
func ( aH * APIHandler ) createDashboards ( w http . ResponseWriter , r * http . Request ) {
var postData map [ string ] interface { }
2022-05-03 15:26:32 +05:30
2021-09-02 13:18:47 +05:30
err := json . NewDecoder ( r . Body ) . Decode ( & postData )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , "Error reading request body" )
2021-09-02 13:18:47 +05:30
return
}
2022-05-03 15:26:32 +05:30
2021-09-02 13:18:47 +05:30
err = dashboards . IsPostDataSane ( & postData )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , "Error reading request body" )
2021-09-02 13:18:47 +05:30
return
}
2023-10-17 17:50:54 +00:00
dash , apiErr := dashboards . CreateDashboard ( r . Context ( ) , postData , aH . featureFlags )
2021-09-02 13:18:47 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2021-09-02 13:18:47 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , dash )
2021-09-02 13:18:47 +05:30
}
2022-08-04 17:24:15 +05:30
func ( aH * APIHandler ) testRule ( w http . ResponseWriter , r * http . Request ) {
defer r . Body . Close ( )
2023-10-19 14:16:20 +05:30
body , err := io . ReadAll ( r . Body )
2022-08-04 17:24:15 +05:30
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error in getting req body in test rule API" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2022-08-04 17:24:15 +05:30
return
}
ctx , cancel := context . WithTimeout ( context . Background ( ) , 1 * time . Minute )
defer cancel ( )
alertCount , apiRrr := aH . ruleManager . TestNotification ( ctx , string ( body ) )
if apiRrr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiRrr , nil )
2022-08-04 17:24:15 +05:30
return
}
response := map [ string ] interface { } {
"alertCount" : alertCount ,
"message" : "notification sent" ,
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , response )
2022-08-04 17:24:15 +05:30
}
2021-11-22 16:15:58 +05:30
func ( aH * APIHandler ) deleteRule ( w http . ResponseWriter , r * http . Request ) {
2022-07-14 11:59:06 +05:30
2021-11-22 16:15:58 +05:30
id := mux . Vars ( r ) [ "id" ]
2023-10-17 17:50:54 +00:00
err := aH . ruleManager . DeleteRule ( r . Context ( ) , id )
2021-11-22 16:15:58 +05:30
2022-07-14 11:59:06 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , "rule successfully deleted" )
2021-11-22 16:15:58 +05:30
}
2022-07-14 11:59:06 +05:30
2022-08-04 11:55:54 +05:30
// patchRule updates only requested changes in the rule
func ( aH * APIHandler ) patchRule ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
defer r . Body . Close ( )
2023-10-19 14:16:20 +05:30
body , err := io . ReadAll ( r . Body )
2022-08-04 11:55:54 +05:30
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error in getting req body of patch rule API\n" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2022-08-04 11:55:54 +05:30
return
}
2023-10-17 17:50:54 +00:00
gettableRule , err := aH . ruleManager . PatchRule ( r . Context ( ) , string ( body ) , id )
2022-08-04 11:55:54 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
2022-08-04 11:55:54 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , gettableRule )
2022-08-04 11:55:54 +05:30
}
2021-11-22 16:15:58 +05:30
func ( aH * APIHandler ) editRule ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
2022-07-14 11:59:06 +05:30
defer r . Body . Close ( )
2023-10-19 14:16:20 +05:30
body , err := io . ReadAll ( r . Body )
2021-11-22 16:15:58 +05:30
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error in getting req body of edit rule API" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2023-10-17 17:50:54 +00:00
err = aH . ruleManager . EditRule ( r . Context ( ) , string ( body ) , id )
2021-11-22 16:15:58 +05:30
2022-07-14 11:59:06 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , "rule successfully edited" )
2021-11-22 16:15:58 +05:30
}
func ( aH * APIHandler ) getChannel ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
2024-09-17 11:41:46 +05:30
channel , apiErrorObj := aH . ruleManager . RuleDB ( ) . GetChannel ( id )
2021-11-22 16:15:58 +05:30
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , channel )
2021-11-22 16:15:58 +05:30
}
func ( aH * APIHandler ) deleteChannel ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
2024-09-17 11:41:46 +05:30
apiErrorObj := aH . ruleManager . RuleDB ( ) . DeleteChannel ( id )
2021-11-22 16:15:58 +05:30
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , "notification channel successfully deleted" )
2021-11-22 16:15:58 +05:30
}
func ( aH * APIHandler ) listChannels ( w http . ResponseWriter , r * http . Request ) {
2024-09-17 11:41:46 +05:30
channels , apiErrorObj := aH . ruleManager . RuleDB ( ) . GetChannels ( )
2021-11-22 16:15:58 +05:30
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , channels )
2021-11-22 16:15:58 +05:30
}
2022-04-22 12:11:19 +05:30
// testChannels sends test alert to all registered channels
func ( aH * APIHandler ) testChannel ( w http . ResponseWriter , r * http . Request ) {
defer r . Body . Close ( )
2023-10-19 14:16:20 +05:30
body , err := io . ReadAll ( r . Body )
2022-04-22 12:11:19 +05:30
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error in getting req body of testChannel API" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2022-04-22 12:11:19 +05:30
return
}
receiver := & am . Receiver { }
if err := json . Unmarshal ( body , receiver ) ; err != nil { // Parse []byte to go struct pointer
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error in parsing req body of testChannel API\n" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2022-04-22 12:11:19 +05:30
return
}
// send alert
apiErrorObj := aH . alertManager . TestReceiver ( receiver )
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2022-04-22 12:11:19 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , "test alert sent" )
2022-04-22 12:11:19 +05:30
}
2021-11-22 16:15:58 +05:30
func ( aH * APIHandler ) editChannel ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
defer r . Body . Close ( )
2023-10-19 14:16:20 +05:30
body , err := io . ReadAll ( r . Body )
2021-11-22 16:15:58 +05:30
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error in getting req body of editChannel API" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-03-28 21:01:57 +05:30
receiver := & am . Receiver { }
2021-11-22 16:15:58 +05:30
if err := json . Unmarshal ( body , receiver ) ; err != nil { // Parse []byte to go struct pointer
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error in parsing req body of editChannel API" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2024-09-17 11:41:46 +05:30
_ , apiErrorObj := aH . ruleManager . RuleDB ( ) . EditChannel ( receiver , id )
2021-11-22 16:15:58 +05:30
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , nil )
2021-11-22 16:15:58 +05:30
}
func ( aH * APIHandler ) createChannel ( w http . ResponseWriter , r * http . Request ) {
defer r . Body . Close ( )
2023-10-19 14:16:20 +05:30
body , err := io . ReadAll ( r . Body )
2021-11-22 16:15:58 +05:30
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error in getting req body of createChannel API" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-03-28 21:01:57 +05:30
receiver := & am . Receiver { }
2021-11-22 16:15:58 +05:30
if err := json . Unmarshal ( body , receiver ) ; err != nil { // Parse []byte to go struct pointer
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error in parsing req body of createChannel API" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2024-09-17 11:41:46 +05:30
_ , apiErrorObj := aH . ruleManager . RuleDB ( ) . CreateChannel ( receiver )
2021-11-22 16:15:58 +05:30
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2021-11-22 16:15:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , nil )
2021-11-22 16:15:58 +05:30
}
2023-08-24 17:14:42 +05:30
func ( aH * APIHandler ) getAlerts ( w http . ResponseWriter , r * http . Request ) {
params := r . URL . Query ( )
amEndpoint := constants . GetAlertManagerApiPrefix ( )
resp , err := http . Get ( amEndpoint + "v1/alerts" + "?" + params . Encode ( ) )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
defer resp . Body . Close ( )
body , err := io . ReadAll ( resp . Body )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , string ( body ) )
}
2021-11-22 16:15:58 +05:30
func ( aH * APIHandler ) createRule ( w http . ResponseWriter , r * http . Request ) {
2022-07-14 11:59:06 +05:30
defer r . Body . Close ( )
2023-10-19 14:16:20 +05:30
body , err := io . ReadAll ( r . Body )
2021-11-22 16:15:58 +05:30
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error in getting req body for create rule API" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2023-11-28 10:44:11 +05:30
rule , err := aH . ruleManager . CreateRule ( r . Context ( ) , string ( body ) )
2022-07-14 11:59:06 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2021-11-22 16:15:58 +05:30
return
}
2023-11-28 10:44:11 +05:30
aH . Respond ( w , rule )
2021-11-22 16:15:58 +05:30
}
2022-08-04 17:24:15 +05:30
2021-08-29 10:28:40 +05:30
func ( aH * APIHandler ) queryRangeMetrics ( w http . ResponseWriter , r * http . Request ) {
query , apiErrorObj := parseQueryRangeRequest ( r )
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2021-08-29 10:28:40 +05:30
return
}
2024-03-27 00:07:29 +05:30
// zap.L().Info(query, apiError)
2021-08-29 10:28:40 +05:30
ctx := r . Context ( )
if to := r . FormValue ( "timeout" ) ; to != "" {
var cancel context . CancelFunc
timeout , err := parseMetricsDuration ( to )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-08-29 10:28:40 +05:30
return
}
ctx , cancel = context . WithTimeout ( ctx , timeout )
defer cancel ( )
}
2022-10-06 20:13:30 +05:30
res , qs , apiError := aH . reader . GetQueryRangeResult ( ctx , query )
2021-08-29 10:28:40 +05:30
if apiError != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiError , nil )
2021-08-29 10:28:40 +05:30
return
}
if res . Err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error in query range metrics" , zap . Error ( res . Err ) )
2021-08-29 10:28:40 +05:30
}
if res . Err != nil {
switch res . Err . ( type ) {
case promql . ErrQueryCanceled :
2024-06-11 20:10:38 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorCanceled , Err : res . Err } , nil )
2021-08-29 10:28:40 +05:30
case promql . ErrQueryTimeout :
2024-06-11 20:10:38 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorTimeout , Err : res . Err } , nil )
2021-08-29 10:28:40 +05:30
}
2024-06-11 20:10:38 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorExec , Err : res . Err } , nil )
2022-12-27 11:28:15 +05:30
return
2021-08-29 10:28:40 +05:30
}
response_data := & model . QueryData {
ResultType : res . Value . Type ( ) ,
Result : res . Value ,
Stats : qs ,
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , response_data )
2021-08-29 10:28:40 +05:30
}
func ( aH * APIHandler ) queryMetrics ( w http . ResponseWriter , r * http . Request ) {
queryParams , apiErrorObj := parseInstantQueryMetricsRequest ( r )
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2021-08-29 10:28:40 +05:30
return
}
2024-03-27 00:07:29 +05:30
// zap.L().Info(query, apiError)
2021-08-29 10:28:40 +05:30
ctx := r . Context ( )
if to := r . FormValue ( "timeout" ) ; to != "" {
var cancel context . CancelFunc
timeout , err := parseMetricsDuration ( to )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-08-29 10:28:40 +05:30
return
}
ctx , cancel = context . WithTimeout ( ctx , timeout )
defer cancel ( )
}
2022-10-06 20:13:30 +05:30
res , qs , apiError := aH . reader . GetInstantQueryMetricsResult ( ctx , queryParams )
2021-08-29 10:28:40 +05:30
if apiError != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiError , nil )
2021-08-29 10:28:40 +05:30
return
}
if res . Err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error in query range metrics" , zap . Error ( res . Err ) )
2021-08-29 10:28:40 +05:30
}
if res . Err != nil {
switch res . Err . ( type ) {
case promql . ErrQueryCanceled :
2024-06-11 20:10:38 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorCanceled , Err : res . Err } , nil )
2021-08-29 10:28:40 +05:30
case promql . ErrQueryTimeout :
2024-06-11 20:10:38 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorTimeout , Err : res . Err } , nil )
2021-08-29 10:28:40 +05:30
}
2024-06-11 20:10:38 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorExec , Err : res . Err } , nil )
2021-08-29 10:28:40 +05:30
}
response_data := & model . QueryData {
ResultType : res . Value . Type ( ) ,
Result : res . Value ,
Stats : qs ,
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , response_data )
2021-08-29 10:28:40 +05:30
}
2021-12-02 18:37:33 +05:30
func ( aH * APIHandler ) submitFeedback ( w http . ResponseWriter , r * http . Request ) {
var postData map [ string ] interface { }
err := json . NewDecoder ( r . Body ) . Decode ( & postData )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , "Error reading request body" )
2021-12-02 18:37:33 +05:30
return
}
message , ok := postData [ "message" ]
if ! ok {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "message not present in request body" ) } , "Error reading message from request body" )
2021-12-02 18:37:33 +05:30
return
}
messageStr := fmt . Sprintf ( "%s" , message )
if len ( messageStr ) == 0 {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "empty message in request body" ) } , "empty message in request body" )
2021-12-02 18:37:33 +05:30
return
}
email := postData [ "email" ]
2022-01-26 21:40:44 +05:30
data := map [ string ] interface { } {
"email" : email ,
"message" : message ,
}
2023-11-16 15:11:38 +05:30
userEmail , err := auth . GetEmailFromJwt ( r . Context ( ) )
if err == nil {
2024-03-28 21:43:41 +05:30
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_EVENT_INPRODUCT_FEEDBACK , data , userEmail , true , false )
}
}
func ( aH * APIHandler ) registerEvent ( w http . ResponseWriter , r * http . Request ) {
request , err := parseRegisterEventRequest ( r )
if aH . HandleError ( w , err , http . StatusBadRequest ) {
return
}
userEmail , err := auth . GetEmailFromJwt ( r . Context ( ) )
if err == nil {
2024-04-01 15:06:38 +05:30
telemetry . GetInstance ( ) . SendEvent ( request . EventName , request . Attributes , userEmail , request . RateLimited , true )
2024-03-28 21:43:41 +05:30
aH . WriteJSON ( w , r , map [ string ] string { "data" : "Event Processed Successfully" } )
} else {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
2023-11-16 15:11:38 +05:30
}
2021-12-02 18:37:33 +05:30
}
2022-08-04 11:57:05 +05:30
func ( aH * APIHandler ) getTopOperations ( w http . ResponseWriter , r * http . Request ) {
2021-01-03 18:15:44 +05:30
2022-08-04 11:57:05 +05:30
query , err := parseGetTopOperationsRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-05-31 11:14:11 +05:30
return
}
2021-01-03 18:15:44 +05:30
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetTopOperations ( r . Context ( ) , query )
2021-01-03 18:15:44 +05:30
2022-10-06 20:13:30 +05:30
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2021-05-31 11:14:11 +05:30
return
}
2021-01-03 18:15:44 +05:30
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2021-01-03 18:15:44 +05:30
2021-05-31 11:14:11 +05:30
}
2021-01-03 18:15:44 +05:30
2021-05-31 11:14:11 +05:30
func ( aH * APIHandler ) getUsage ( w http . ResponseWriter , r * http . Request ) {
2021-01-03 18:15:44 +05:30
2021-05-31 11:14:11 +05:30
query , err := parseGetUsageRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-05-31 11:14:11 +05:30
return
}
2021-01-03 18:15:44 +05:30
2022-10-06 20:13:30 +05:30
result , err := aH . reader . GetUsage ( r . Context ( ) , query )
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-05-30 11:14:55 +05:30
return
}
2021-04-26 21:55:11 +05:30
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2021-04-26 21:55:11 +05:30
2021-05-30 11:14:55 +05:30
}
2021-04-26 21:55:11 +05:30
2022-08-04 11:57:05 +05:30
func ( aH * APIHandler ) getServicesTopLevelOps ( w http . ResponseWriter , r * http . Request ) {
2024-03-12 17:22:48 +05:30
var start , end time . Time
2024-07-30 02:02:50 +05:30
var services [ ] string
2024-03-12 17:22:48 +05:30
2024-07-30 02:02:50 +05:30
type topLevelOpsParams struct {
Service string ` json:"service" `
Start string ` json:"start" `
End string ` json:"end" `
}
var params topLevelOpsParams
err := json . NewDecoder ( r . Body ) . Decode ( & params )
if err != nil {
zap . L ( ) . Error ( "Error in getting req body for get top operations API" , zap . Error ( err ) )
}
if params . Service != "" {
services = [ ] string { params . Service }
}
startEpoch := params . Start
if startEpoch != "" {
startEpochInt , err := strconv . ParseInt ( startEpoch , 10 , 64 )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , "Error reading start time" )
return
}
start = time . Unix ( 0 , startEpochInt )
}
endEpoch := params . End
if endEpoch != "" {
endEpochInt , err := strconv . ParseInt ( endEpoch , 10 , 64 )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , "Error reading end time" )
return
}
end = time . Unix ( 0 , endEpochInt )
}
result , apiErr := aH . reader . GetTopLevelOperations ( r . Context ( ) , aH . skipConfig , start , end , services )
2022-08-04 11:57:05 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-08-04 11:57:05 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-08-04 11:57:05 +05:30
}
2021-01-03 18:15:44 +05:30
func ( aH * APIHandler ) getServices ( w http . ResponseWriter , r * http . Request ) {
query , err := parseGetServicesRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-01-03 18:15:44 +05:30
return
}
2023-06-30 06:58:22 +05:30
result , apiErr := aH . reader . GetServices ( r . Context ( ) , query , aH . skipConfig )
2022-10-06 20:13:30 +05:30
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2021-01-03 18:15:44 +05:30
return
}
2022-01-26 21:40:44 +05:30
data := map [ string ] interface { } {
"number" : len ( * result ) ,
}
2023-11-16 15:11:38 +05:30
userEmail , err := auth . GetEmailFromJwt ( r . Context ( ) )
if err == nil {
2024-03-28 21:43:41 +05:30
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_EVENT_NUMBER_OF_SERVICES , data , userEmail , true , false )
2023-11-16 15:11:38 +05:30
}
2022-01-26 21:40:44 +05:30
2022-12-28 02:33:21 +05:30
if ( data [ "number" ] != 0 ) && ( data [ "number" ] != telemetry . DEFAULT_NUMBER_OF_SERVICES ) {
2022-12-28 02:16:46 +05:30
telemetry . GetInstance ( ) . AddActiveTracesUser ( )
}
2021-01-03 18:15:44 +05:30
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2021-01-03 18:15:44 +05:30
}
2021-05-05 00:03:57 +05:30
2022-08-04 12:38:53 +05:30
func ( aH * APIHandler ) dependencyGraph ( w http . ResponseWriter , r * http . Request ) {
2021-05-31 11:14:11 +05:30
query , err := parseGetServicesRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-05-31 11:14:11 +05:30
return
}
2021-05-05 00:03:57 +05:30
2022-10-06 20:13:30 +05:30
result , err := aH . reader . GetDependencyGraph ( r . Context ( ) , query )
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-05-03 11:20:57 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-05-03 11:20:57 +05:30
}
func ( aH * APIHandler ) getServicesList ( w http . ResponseWriter , r * http . Request ) {
2022-10-06 20:13:30 +05:30
result , err := aH . reader . GetServicesList ( r . Context ( ) )
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-05-31 11:14:11 +05:30
return
}
2021-05-05 00:03:57 +05:30
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-05-03 11:20:57 +05:30
2021-05-31 11:14:11 +05:30
}
2021-05-05 00:03:57 +05:30
2022-11-24 18:18:19 +05:30
func ( aH * APIHandler ) SearchTraces ( w http . ResponseWriter , r * http . Request ) {
2021-05-05 00:03:57 +05:30
2024-05-27 17:20:45 +05:30
params , err := ParseSearchTracesParams ( r )
2022-11-24 18:18:19 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , "Error reading params" )
return
}
2021-01-03 18:15:44 +05:30
2024-05-27 17:20:45 +05:30
result , err := aH . reader . SearchTraces ( r . Context ( ) , params , nil )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-05-31 11:14:11 +05:30
return
}
2021-01-03 18:15:44 +05:30
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2021-01-03 18:15:44 +05:30
2021-05-31 11:14:11 +05:30
}
2021-01-03 18:15:44 +05:30
2022-07-13 15:55:43 +05:30
func ( aH * APIHandler ) listErrors ( w http . ResponseWriter , r * http . Request ) {
2022-01-21 00:31:58 +05:30
2022-07-13 15:55:43 +05:30
query , err := parseListErrorsRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-21 00:31:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . ListErrors ( r . Context ( ) , query )
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2022-01-21 00:31:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-01-21 00:31:58 +05:30
}
2022-07-13 15:55:43 +05:30
func ( aH * APIHandler ) countErrors ( w http . ResponseWriter , r * http . Request ) {
2022-01-21 00:31:58 +05:30
2022-07-13 15:55:43 +05:30
query , err := parseCountErrorsRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-21 00:31:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . CountErrors ( r . Context ( ) , query )
2022-07-13 15:55:43 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-01-21 00:31:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-07-13 15:55:43 +05:30
}
func ( aH * APIHandler ) getErrorFromErrorID ( w http . ResponseWriter , r * http . Request ) {
query , err := parseGetErrorRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-07-13 15:55:43 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetErrorFromErrorID ( r . Context ( ) , query )
2022-07-13 15:55:43 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-07-13 15:55:43 +05:30
return
}
2022-01-21 00:31:58 +05:30
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-01-21 00:31:58 +05:30
}
2022-07-13 15:55:43 +05:30
func ( aH * APIHandler ) getNextPrevErrorIDs ( w http . ResponseWriter , r * http . Request ) {
2022-01-21 00:31:58 +05:30
2022-07-13 15:55:43 +05:30
query , err := parseGetErrorRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-21 00:31:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetNextPrevErrorIDs ( r . Context ( ) , query )
2022-07-13 15:55:43 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-01-21 00:31:58 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-07-13 15:55:43 +05:30
}
func ( aH * APIHandler ) getErrorFromGroupID ( w http . ResponseWriter , r * http . Request ) {
2022-01-21 00:31:58 +05:30
2022-07-13 15:55:43 +05:30
query , err := parseGetErrorRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-07-13 15:55:43 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetErrorFromGroupID ( r . Context ( ) , query )
2022-07-13 15:55:43 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-07-13 15:55:43 +05:30
return
}
2022-01-21 00:31:58 +05:30
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-01-21 00:31:58 +05:30
}
2021-10-20 13:18:19 +05:30
func ( aH * APIHandler ) setTTL ( w http . ResponseWriter , r * http . Request ) {
2022-03-21 23:58:56 +05:30
ttlParams , err := parseTTLParams ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-10-20 13:18:19 +05:30
return
}
2022-05-25 16:55:30 +05:30
// Context is not used here as TTL is long duration DB operation
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . SetTTL ( context . Background ( ) , ttlParams )
2022-05-25 16:55:30 +05:30
if apiErr != nil {
if apiErr . Typ == model . ErrorConflict {
2022-10-06 20:13:30 +05:30
aH . HandleError ( w , apiErr . Err , http . StatusConflict )
2022-05-25 16:55:30 +05:30
} else {
2022-10-06 20:13:30 +05:30
aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError )
2022-05-25 16:55:30 +05:30
}
2021-10-20 13:18:19 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2021-10-20 13:18:19 +05:30
}
func ( aH * APIHandler ) getTTL ( w http . ResponseWriter , r * http . Request ) {
ttlParams , err := parseGetTTL ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-10-20 13:18:19 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetTTL ( r . Context ( ) , ttlParams )
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2021-10-20 13:18:19 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2021-10-20 13:18:19 +05:30
}
2022-03-21 23:58:56 +05:30
func ( aH * APIHandler ) getDisks ( w http . ResponseWriter , r * http . Request ) {
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetDisks ( context . Background ( ) )
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2022-03-21 23:58:56 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-03-21 23:58:56 +05:30
}
2022-05-03 15:26:32 +05:30
func ( aH * APIHandler ) getVersion ( w http . ResponseWriter , r * http . Request ) {
version := version . GetVersion ( )
2023-02-24 14:57:07 +05:30
versionResponse := model . GetVersionResponse {
Version : version ,
EE : "Y" ,
SetupCompleted : aH . SetupCompleted ,
}
aH . WriteJSON ( w , r , versionResponse )
2022-05-03 15:26:32 +05:30
}
2022-01-26 21:40:44 +05:30
2022-11-09 08:30:00 +05:30
func ( aH * APIHandler ) getFeatureFlags ( w http . ResponseWriter , r * http . Request ) {
2023-05-17 16:10:43 +05:30
featureSet , err := aH . FF ( ) . GetFeatureFlags ( )
if err != nil {
aH . HandleError ( w , err , http . StatusInternalServerError )
return
}
2023-07-14 11:31:44 +05:30
if aH . preferSpanMetrics {
for idx := range featureSet {
feature := & featureSet [ idx ]
if feature . Name == model . UseSpanMetrics {
featureSet [ idx ] . Active = true
}
}
}
2022-11-09 08:30:00 +05:30
aH . Respond ( w , featureSet )
}
func ( aH * APIHandler ) FF ( ) interfaces . FeatureLookup {
return aH . featureFlags
}
func ( aH * APIHandler ) CheckFeature ( f string ) bool {
err := aH . FF ( ) . CheckFeature ( f )
return err == nil
}
2022-11-14 14:29:13 +05:30
func ( aH * APIHandler ) getConfigs ( w http . ResponseWriter , r * http . Request ) {
configs , err := signozio . FetchDynamicConfigs ( )
if err != nil {
aH . HandleError ( w , err , http . StatusInternalServerError )
return
}
aH . Respond ( w , configs )
}
2023-02-15 00:37:57 +05:30
// getHealth is used to check the health of the service.
// 'live' query param can be used to check liveliness of
// the service by checking the database connection.
func ( aH * APIHandler ) getHealth ( w http . ResponseWriter , r * http . Request ) {
_ , ok := r . URL . Query ( ) [ "live" ]
if ok {
err := aH . reader . CheckClickHouse ( r . Context ( ) )
if err != nil {
2023-02-28 23:42:21 +05:30
RespondError ( w , & model . ApiError { Err : err , Typ : model . ErrorStatusServiceUnavailable } , nil )
2023-02-15 00:37:57 +05:30
return
}
}
aH . WriteJSON ( w , r , map [ string ] string { "status" : "ok" } )
}
2022-05-03 15:26:32 +05:30
// inviteUser is used to invite a user. It is used by an admin api.
func ( aH * APIHandler ) inviteUser ( w http . ResponseWriter , r * http . Request ) {
req , err := parseInviteRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-26 21:40:44 +05:30
return
}
2023-11-16 15:11:38 +05:30
resp , err := auth . Invite ( r . Context ( ) , req )
2022-05-03 15:26:32 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Err : err , Typ : model . ErrorInternal } , nil )
2022-01-26 21:40:44 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , resp )
2022-05-03 15:26:32 +05:30
}
2022-01-26 21:40:44 +05:30
2024-10-17 18:41:31 +05:30
func ( aH * APIHandler ) inviteUsers ( w http . ResponseWriter , r * http . Request ) {
ctx := r . Context ( )
req , err := parseInviteUsersRequest ( r )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
response , err := auth . InviteUsers ( ctx , req )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
// Check the response status and set the appropriate HTTP status code
if response . Status == "failure" {
2024-10-23 23:43:46 +05:30
w . WriteHeader ( http . StatusBadRequest ) // 400 Bad Request for failure
2024-10-17 18:41:31 +05:30
} else if response . Status == "partial_success" {
w . WriteHeader ( http . StatusPartialContent ) // 206 Partial Content
} else {
w . WriteHeader ( http . StatusOK ) // 200 OK for success
}
aH . WriteJSON ( w , r , response )
}
2022-05-03 15:26:32 +05:30
// getInvite returns the invite object details for the given invite token. We do not need to
// protect this API because invite token itself is meant to be private.
func ( aH * APIHandler ) getInvite ( w http . ResponseWriter , r * http . Request ) {
token := mux . Vars ( r ) [ "token" ]
resp , err := auth . GetInvite ( context . Background ( ) , token )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Err : err , Typ : model . ErrorNotFound } , nil )
2022-05-03 15:26:32 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , resp )
2022-05-03 15:26:32 +05:30
}
// revokeInvite is used to revoke an invite.
func ( aH * APIHandler ) revokeInvite ( w http . ResponseWriter , r * http . Request ) {
email := mux . Vars ( r ) [ "email" ]
2023-11-16 15:11:38 +05:30
if err := auth . RevokeInvite ( r . Context ( ) , email ) ; err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Err : err , Typ : model . ErrorInternal } , nil )
2022-05-03 15:26:32 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , map [ string ] string { "data" : "invite revoked successfully" } )
2022-01-26 21:40:44 +05:30
}
2022-05-03 15:26:32 +05:30
// listPendingInvites is used to list the pending invites.
func ( aH * APIHandler ) listPendingInvites ( w http . ResponseWriter , r * http . Request ) {
ctx := context . Background ( )
invites , err := dao . DB ( ) . GetInvites ( ctx )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , err , nil )
2022-05-03 15:26:32 +05:30
return
}
// TODO(Ahsan): Querying org name based on orgId for each invite is not a good idea. Either
// we should include org name field in the invite table, or do a join query.
var resp [ ] * model . InvitationResponseObject
for _ , inv := range invites {
org , apiErr := dao . DB ( ) . GetOrg ( ctx , inv . OrgId )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-05-03 15:26:32 +05:30
}
resp = append ( resp , & model . InvitationResponseObject {
Name : inv . Name ,
Email : inv . Email ,
Token : inv . Token ,
CreatedAt : inv . CreatedAt ,
Role : inv . Role ,
Organization : org . Name ,
} )
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , resp )
}
// Register extends registerUser for non-internal packages
func ( aH * APIHandler ) Register ( w http . ResponseWriter , r * http . Request ) {
aH . registerUser ( w , r )
2022-01-26 21:40:44 +05:30
}
2022-05-03 15:26:32 +05:30
func ( aH * APIHandler ) registerUser ( w http . ResponseWriter , r * http . Request ) {
req , err := parseRegisterRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-26 21:40:44 +05:30
return
}
2022-10-06 20:13:30 +05:30
_ , apiErr := auth . Register ( context . Background ( ) , req )
2022-05-03 15:26:32 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-05-03 15:26:32 +05:30
return
}
2023-02-24 14:57:07 +05:30
if ! aH . SetupCompleted {
// since the first user is now created, we can disable self-registration as
// from here onwards, we expect admin (owner) to invite other users.
aH . SetupCompleted = true
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , nil )
2022-05-03 15:26:32 +05:30
}
2023-09-12 12:53:46 +05:30
func ( aH * APIHandler ) precheckLogin ( w http . ResponseWriter , r * http . Request ) {
email := r . URL . Query ( ) . Get ( "email" )
sourceUrl := r . URL . Query ( ) . Get ( "ref" )
resp , apierr := aH . appDao . PrecheckLogin ( context . Background ( ) , email , sourceUrl )
if apierr != nil {
RespondError ( w , apierr , resp )
return
}
aH . Respond ( w , resp )
}
2022-05-03 15:26:32 +05:30
func ( aH * APIHandler ) loginUser ( w http . ResponseWriter , r * http . Request ) {
req , err := parseLoginRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-05-03 15:26:32 +05:30
return
}
// c, err := r.Cookie("refresh-token")
// if err != nil {
// if err != http.ErrNoCookie {
// w.WriteHeader(http.StatusBadRequest)
// return
// }
// }
// if c != nil {
// req.RefreshToken = c.Value
// }
resp , err := auth . Login ( context . Background ( ) , req )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusUnauthorized ) {
2022-05-03 15:26:32 +05:30
return
}
// http.SetCookie(w, &http.Cookie{
// Name: "refresh-token",
// Value: resp.RefreshJwt,
// Expires: time.Unix(resp.RefreshJwtExpiry, 0),
// HttpOnly: true,
// })
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , resp )
2022-05-03 15:26:32 +05:30
}
func ( aH * APIHandler ) listUsers ( w http . ResponseWriter , r * http . Request ) {
users , err := dao . DB ( ) . GetUsers ( context . Background ( ) )
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "[listUsers] Failed to query list of users" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , err , nil )
2022-05-03 15:26:32 +05:30
return
}
// mask the password hash
2022-05-04 14:50:15 +05:30
for i := range users {
users [ i ] . Password = ""
2022-05-03 15:26:32 +05:30
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , users )
2022-05-03 15:26:32 +05:30
}
func ( aH * APIHandler ) getUser ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
ctx := context . Background ( )
user , err := dao . DB ( ) . GetUser ( ctx , id )
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "[getUser] Failed to query user" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , err , "Failed to get user" )
2022-05-03 15:26:32 +05:30
return
}
2022-05-04 14:50:15 +05:30
if user == nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError {
2022-05-04 14:50:15 +05:30
Typ : model . ErrorInternal ,
2024-06-11 20:10:38 +05:30
Err : errors . New ( "user not found" ) ,
2022-05-04 14:50:15 +05:30
} , nil )
return
2022-05-03 15:26:32 +05:30
}
2022-05-04 14:50:15 +05:30
// No need to send password hash for the user object.
user . Password = ""
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , user )
2022-05-03 15:26:32 +05:30
}
// editUser only changes the user's Name and ProfilePictureURL. It is intentionally designed
// to not support update of orgId, Password, createdAt for the sucurity reasons.
func ( aH * APIHandler ) editUser ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
update , err := parseUserRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-05-03 15:26:32 +05:30
return
}
ctx := context . Background ( )
old , apiErr := dao . DB ( ) . GetUser ( ctx , id )
if apiErr != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "[editUser] Failed to query user" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-05-03 15:26:32 +05:30
return
}
if len ( update . Name ) > 0 {
old . Name = update . Name
}
2023-08-12 10:10:25 +05:30
if len ( update . ProfilePictureURL ) > 0 {
old . ProfilePictureURL = update . ProfilePictureURL
2022-05-03 15:26:32 +05:30
}
_ , apiErr = dao . DB ( ) . EditUser ( ctx , & model . User {
2023-08-12 10:10:25 +05:30
Id : old . Id ,
Name : old . Name ,
OrgId : old . OrgId ,
Email : old . Email ,
Password : old . Password ,
CreatedAt : old . CreatedAt ,
ProfilePictureURL : old . ProfilePictureURL ,
2022-05-03 15:26:32 +05:30
} )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , nil )
2022-05-03 15:26:32 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , map [ string ] string { "data" : "user updated successfully" } )
2022-05-03 15:26:32 +05:30
}
func ( aH * APIHandler ) deleteUser ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
// Query for the user's group, and the admin's group. If the user belongs to the admin group
// and is the last user then don't let the deletion happen. Otherwise, the system will become
// admin less and hence inaccessible.
ctx := context . Background ( )
user , apiErr := dao . DB ( ) . GetUser ( ctx , id )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to get user's group" )
2022-05-03 15:26:32 +05:30
return
}
2022-05-04 14:50:15 +05:30
if user == nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError {
2022-05-04 14:50:15 +05:30
Typ : model . ErrorNotFound ,
2024-06-11 20:10:38 +05:30
Err : errors . New ( "no user found" ) ,
2022-05-04 14:50:15 +05:30
} , nil )
return
}
2022-05-03 15:26:32 +05:30
adminGroup , apiErr := dao . DB ( ) . GetGroupByName ( ctx , constants . AdminGroup )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to get admin group" )
2022-05-03 15:26:32 +05:30
return
}
adminUsers , apiErr := dao . DB ( ) . GetUsersByGroup ( ctx , adminGroup . Id )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to get admin group users" )
2022-05-03 15:26:32 +05:30
return
}
if user . GroupId == adminGroup . Id && len ( adminUsers ) == 1 {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError {
2022-05-03 15:26:32 +05:30
Typ : model . ErrorInternal ,
Err : errors . New ( "cannot delete the last admin user" ) } , nil )
return
}
err := dao . DB ( ) . DeleteUser ( ctx , id )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , err , "Failed to delete user" )
2022-05-03 15:26:32 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , map [ string ] string { "data" : "user deleted successfully" } )
2022-05-03 15:26:32 +05:30
}
2022-12-09 20:16:09 +05:30
// addUserFlag patches a user flags with the changes
func ( aH * APIHandler ) patchUserFlag ( w http . ResponseWriter , r * http . Request ) {
// read user id from path var
userId := mux . Vars ( r ) [ "id" ]
// read input into user flag
defer r . Body . Close ( )
2023-10-19 14:16:20 +05:30
b , err := io . ReadAll ( r . Body )
2022-12-09 20:16:09 +05:30
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "failed read user flags from http request for userId " , zap . String ( "userId" , userId ) , zap . Error ( err ) )
2022-12-09 20:16:09 +05:30
RespondError ( w , model . BadRequestStr ( "received user flags in invalid format" ) , nil )
return
}
flags := make ( map [ string ] string , 0 )
err = json . Unmarshal ( b , & flags )
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "failed parsing user flags for userId " , zap . String ( "userId" , userId ) , zap . Error ( err ) )
2022-12-09 20:16:09 +05:30
RespondError ( w , model . BadRequestStr ( "received user flags in invalid format" ) , nil )
return
}
newflags , apiError := dao . DB ( ) . UpdateUserFlags ( r . Context ( ) , userId , flags )
if ! apiError . IsNil ( ) {
RespondError ( w , apiError , nil )
return
}
aH . Respond ( w , newflags )
}
2022-05-03 15:26:32 +05:30
func ( aH * APIHandler ) getRole ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
user , err := dao . DB ( ) . GetUser ( context . Background ( ) , id )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , err , "Failed to get user's group" )
2022-05-03 15:26:32 +05:30
return
}
if user == nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError {
2022-05-03 15:26:32 +05:30
Typ : model . ErrorNotFound ,
2024-06-11 20:10:38 +05:30
Err : errors . New ( "no user found" ) ,
2022-05-03 15:26:32 +05:30
} , nil )
return
}
group , err := dao . DB ( ) . GetGroup ( context . Background ( ) , user . GroupId )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , err , "Failed to get group" )
2022-05-03 15:26:32 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , & model . UserRole { UserId : id , GroupName : group . Name } )
2022-05-03 15:26:32 +05:30
}
func ( aH * APIHandler ) editRole ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
req , err := parseUserRoleRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-05-03 15:26:32 +05:30
return
}
ctx := context . Background ( )
newGroup , apiErr := dao . DB ( ) . GetGroupByName ( ctx , req . GroupName )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to get user's group" )
2022-05-03 15:26:32 +05:30
return
}
if newGroup == nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Specified group is not present" )
2022-05-03 15:26:32 +05:30
return
}
user , apiErr := dao . DB ( ) . GetUser ( ctx , id )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to fetch user group" )
2022-05-03 15:26:32 +05:30
return
}
// Make sure that the request is not demoting the last admin user.
if user . GroupId == auth . AuthCacheObj . AdminGroupId {
adminUsers , apiErr := dao . DB ( ) . GetUsersByGroup ( ctx , auth . AuthCacheObj . AdminGroupId )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to fetch adminUsers" )
2022-05-03 15:26:32 +05:30
return
}
if len ( adminUsers ) == 1 {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError {
2024-06-11 20:10:38 +05:30
Err : errors . New ( "cannot demote the last admin" ) ,
2022-05-03 15:26:32 +05:30
Typ : model . ErrorInternal } , nil )
return
}
}
apiErr = dao . DB ( ) . UpdateUserGroup ( context . Background ( ) , user . Id , newGroup . Id )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to add user to group" )
2022-05-03 15:26:32 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , map [ string ] string { "data" : "user group updated successfully" } )
2022-05-03 15:26:32 +05:30
}
func ( aH * APIHandler ) getOrgs ( w http . ResponseWriter , r * http . Request ) {
orgs , apiErr := dao . DB ( ) . GetOrgs ( context . Background ( ) )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to fetch orgs from the DB" )
2022-05-03 15:26:32 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , orgs )
2022-05-03 15:26:32 +05:30
}
func ( aH * APIHandler ) getOrg ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
org , apiErr := dao . DB ( ) . GetOrg ( context . Background ( ) , id )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to fetch org from the DB" )
2022-05-03 15:26:32 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , org )
2022-05-03 15:26:32 +05:30
}
func ( aH * APIHandler ) editOrg ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
req , err := parseEditOrgRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-05-03 15:26:32 +05:30
return
}
req . Id = id
if apiErr := dao . DB ( ) . EditOrg ( context . Background ( ) , req ) ; apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to update org in the DB" )
2022-01-26 21:40:44 +05:30
return
}
2022-02-25 20:40:38 +05:30
data := map [ string ] interface { } {
2022-05-04 14:27:32 +05:30
"hasOptedUpdates" : req . HasOptedUpdates ,
"isAnonymous" : req . IsAnonymous ,
"organizationName" : req . Name ,
2022-02-25 20:40:38 +05:30
}
2023-11-16 15:11:38 +05:30
userEmail , err := auth . GetEmailFromJwt ( r . Context ( ) )
2024-06-11 20:10:38 +05:30
if err != nil {
zap . L ( ) . Error ( "failed to get user email from jwt" , zap . Error ( err ) )
}
2024-03-28 21:43:41 +05:30
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_EVENT_ORG_SETTINGS , data , userEmail , true , false )
2022-01-26 21:40:44 +05:30
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , map [ string ] string { "data" : "org updated successfully" } )
2022-01-26 21:40:44 +05:30
}
2022-05-03 15:26:32 +05:30
func ( aH * APIHandler ) getOrgUsers ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
users , apiErr := dao . DB ( ) . GetUsersByOrg ( context . Background ( ) , id )
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to fetch org users from the DB" )
2022-05-03 15:26:32 +05:30
return
}
// mask the password hash
2022-05-04 14:50:15 +05:30
for i := range users {
users [ i ] . Password = ""
2022-05-03 15:26:32 +05:30
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , users )
2022-01-26 21:40:44 +05:30
}
2022-05-03 15:26:32 +05:30
func ( aH * APIHandler ) getResetPasswordToken ( w http . ResponseWriter , r * http . Request ) {
id := mux . Vars ( r ) [ "id" ]
resp , err := auth . CreateResetPasswordToken ( context . Background ( ) , id )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError {
2022-05-03 15:26:32 +05:30
Typ : model . ErrorInternal ,
Err : err } , "Failed to create reset token entry in the DB" )
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , resp )
2022-05-03 15:26:32 +05:30
}
2022-01-26 21:40:44 +05:30
2022-05-03 15:26:32 +05:30
func ( aH * APIHandler ) resetPassword ( w http . ResponseWriter , r * http . Request ) {
req , err := parseResetPasswordRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-05-03 15:26:32 +05:30
return
}
2022-01-26 21:40:44 +05:30
2022-05-03 15:26:32 +05:30
if err := auth . ResetPassword ( context . Background ( ) , req ) ; err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "resetPassword failed" , zap . Error ( err ) )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusInternalServerError ) {
2022-05-03 15:26:32 +05:30
return
}
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , map [ string ] string { "data" : "password reset successfully" } )
2022-05-03 15:26:32 +05:30
}
func ( aH * APIHandler ) changePassword ( w http . ResponseWriter , r * http . Request ) {
req , err := parseChangePasswordRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-05-03 15:26:32 +05:30
return
}
2024-03-26 06:20:35 +05:30
if apiErr := auth . ChangePassword ( context . Background ( ) , req ) ; apiErr != nil {
RespondError ( w , apiErr , nil )
return
2022-05-03 15:26:32 +05:30
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , map [ string ] string { "data" : "password changed successfully" } )
2022-01-26 21:40:44 +05:30
}
2021-05-27 12:52:34 +05:30
// func (aH *APIHandler) getApplicationPercentiles(w http.ResponseWriter, r *http.Request) {
// // vars := mux.Vars(r)
2021-01-03 18:15:44 +05:30
2021-05-27 12:52:34 +05:30
// query, err := parseApplicationPercentileRequest(r)
2022-10-06 20:13:30 +05:30
// if aH.HandleError(w, err, http.StatusBadRequest) {
2021-05-27 12:52:34 +05:30
// return
// }
2021-01-03 18:15:44 +05:30
2022-10-06 20:13:30 +05:30
// result, err := aH.reader.GetApplicationPercentiles(context.Background(), query)
// if aH.HandleError(w, err, http.StatusBadRequest) {
2021-05-27 12:52:34 +05:30
// return
// }
2022-10-06 20:13:30 +05:30
// aH.WriteJSON(w, r, result)
2021-05-27 12:52:34 +05:30
// }
2021-01-03 18:15:44 +05:30
2022-10-06 20:13:30 +05:30
func ( aH * APIHandler ) HandleError ( w http . ResponseWriter , err error , statusCode int ) bool {
2021-01-03 18:15:44 +05:30
if err == nil {
return false
}
if statusCode == http . StatusInternalServerError {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "HTTP handler, Internal Server Error" , zap . Error ( err ) )
2021-01-03 18:15:44 +05:30
}
structuredResp := structuredResponse {
Errors : [ ] structuredError {
{
Code : statusCode ,
Msg : err . Error ( ) ,
} ,
} ,
}
resp , _ := json . Marshal ( & structuredResp )
http . Error ( w , string ( resp ) , statusCode )
return true
}
2022-10-06 20:13:30 +05:30
func ( aH * APIHandler ) WriteJSON ( w http . ResponseWriter , r * http . Request , response interface { } ) {
2021-01-03 18:15:44 +05:30
marshall := json . Marshal
if prettyPrint := r . FormValue ( "pretty" ) ; prettyPrint != "" && prettyPrint != "false" {
marshall = func ( v interface { } ) ( [ ] byte , error ) {
return json . MarshalIndent ( v , "" , " " )
}
}
resp , _ := marshall ( response )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . Write ( resp )
}
2022-07-12 16:38:26 +05:30
2024-07-26 11:50:02 +05:30
// RegisterMessagingQueuesRoutes adds messaging-queues routes
func ( aH * APIHandler ) RegisterMessagingQueuesRoutes ( router * mux . Router , am * AuthMiddleware ) {
2024-10-04 01:10:13 +05:30
2024-07-26 11:50:02 +05:30
// SubRouter for kafka
2024-10-04 01:10:13 +05:30
kafkaRouter := router . PathPrefix ( "/api/v1/messaging-queues/kafka" ) . Subrouter ( )
2024-10-17 19:44:42 +05:30
onboardingRouter := kafkaRouter . PathPrefix ( "/onboarding" ) . Subrouter ( )
onboardingRouter . HandleFunc ( "/producers" , am . ViewAccess ( aH . onboardProducers ) ) . Methods ( http . MethodPost )
onboardingRouter . HandleFunc ( "/consumers" , am . ViewAccess ( aH . onboardConsumers ) ) . Methods ( http . MethodPost )
onboardingRouter . HandleFunc ( "/kafka" , am . ViewAccess ( aH . onboardKafka ) ) . Methods ( http . MethodPost )
partitionLatency := kafkaRouter . PathPrefix ( "/partition-latency" ) . Subrouter ( )
partitionLatency . HandleFunc ( "/overview" , am . ViewAccess ( aH . getPartitionOverviewLatencyData ) ) . Methods ( http . MethodPost )
partitionLatency . HandleFunc ( "/consumer" , am . ViewAccess ( aH . getConsumerPartitionLatencyData ) ) . Methods ( http . MethodPost )
2024-10-04 01:10:13 +05:30
consumerLagRouter := kafkaRouter . PathPrefix ( "/consumer-lag" ) . Subrouter ( )
consumerLagRouter . HandleFunc ( "/producer-details" , am . ViewAccess ( aH . getProducerData ) ) . Methods ( http . MethodPost )
consumerLagRouter . HandleFunc ( "/consumer-details" , am . ViewAccess ( aH . getConsumerData ) ) . Methods ( http . MethodPost )
consumerLagRouter . HandleFunc ( "/network-latency" , am . ViewAccess ( aH . getNetworkData ) ) . Methods ( http . MethodPost )
2024-07-26 11:50:02 +05:30
2024-10-17 19:44:42 +05:30
topicThroughput := kafkaRouter . PathPrefix ( "/topic-throughput" ) . Subrouter ( )
topicThroughput . HandleFunc ( "/producer" , am . ViewAccess ( aH . getProducerThroughputOverview ) ) . Methods ( http . MethodPost )
topicThroughput . HandleFunc ( "/producer-details" , am . ViewAccess ( aH . getProducerThroughputDetails ) ) . Methods ( http . MethodPost )
topicThroughput . HandleFunc ( "/consumer" , am . ViewAccess ( aH . getConsumerThroughputOverview ) ) . Methods ( http . MethodPost )
topicThroughput . HandleFunc ( "/consumer-details" , am . ViewAccess ( aH . getConsumerThroughputDetails ) ) . Methods ( http . MethodPost )
spanEvaluation := kafkaRouter . PathPrefix ( "/span" ) . Subrouter ( )
spanEvaluation . HandleFunc ( "/evaluation" , am . ViewAccess ( aH . getProducerConsumerEval ) ) . Methods ( http . MethodPost )
2024-07-26 11:50:02 +05:30
// for other messaging queues, add SubRouters here
}
2024-08-27 18:27:44 +05:30
// not using md5 hashing as the plain string would work
2024-10-17 19:44:42 +05:30
func uniqueIdentifier ( params [ ] string , separator string ) string {
return strings . Join ( params , separator )
2024-08-27 18:27:44 +05:30
}
2024-09-30 18:13:39 +05:30
func ( aH * APIHandler ) onboardProducers (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-10-04 01:10:13 +05:30
chq , err := mq . BuildClickHouseQuery ( messagingQueue , mq . KafkaQueue , "onboard_producers" )
2024-09-30 18:13:39 +05:30
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-10-04 01:10:13 +05:30
results , err := aH . reader . GetListResultV3 ( r . Context ( ) , chq . Query )
2024-09-30 18:13:39 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2024-10-04 01:10:13 +05:30
RespondError ( w , apiErrObj , err )
2024-09-30 18:13:39 +05:30
return
}
2024-10-04 01:10:13 +05:30
var entries [ ] mq . OnboardingResponse
2024-09-30 18:13:39 +05:30
2024-10-04 01:10:13 +05:30
for _ , result := range results {
2024-09-30 18:13:39 +05:30
2024-10-04 01:10:13 +05:30
for key , value := range result . Data {
var message , attribute , status string
intValue := int ( * value . ( * uint8 ) )
if key == "entries" {
attribute = "telemetry ingestion"
if intValue != 0 {
entries = nil
entry := mq . OnboardingResponse {
Attribute : attribute ,
Message : "No data available in the given time range" ,
Status : "0" ,
}
entries = append ( entries , entry )
break
} else {
status = "1"
}
} else if key == "queue" {
attribute = "messaging.system"
if intValue != 0 {
status = "0"
message = "messaging.system attribute is not present or not equal to kafka in your spans"
} else {
status = "1"
}
} else if key == "kind" {
attribute = "kind"
if intValue != 0 {
status = "0"
message = "check if your producer spans has kind=4 as attribute"
} else {
status = "1"
}
} else if key == "destination" {
attribute = "messaging.destination.name"
if intValue != 0 {
status = "0"
message = "messaging.destination.name attribute is not present in your spans"
} else {
status = "1"
}
} else if key == "partition" {
attribute = "messaging.destination.partition.id"
if intValue != 0 {
status = "0"
message = "messaging.destination.partition.id attribute is not present in your spans"
} else {
status = "1"
2024-09-30 18:13:39 +05:30
}
}
2024-10-04 01:10:13 +05:30
entry := mq . OnboardingResponse {
Attribute : attribute ,
Message : message ,
Status : status ,
}
2024-09-30 18:13:39 +05:30
2024-10-04 01:10:13 +05:30
entries = append ( entries , entry )
2024-09-30 18:13:39 +05:30
}
}
2024-10-04 01:10:13 +05:30
sort . Slice ( entries , func ( i , j int ) bool {
return entries [ i ] . Attribute < entries [ j ] . Attribute
} )
aH . Respond ( w , entries )
2024-09-30 18:13:39 +05:30
}
func ( aH * APIHandler ) onboardConsumers (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-10-04 01:44:52 +05:30
chq , err := mq . BuildClickHouseQuery ( messagingQueue , mq . KafkaQueue , "onboard_consumers" )
2024-10-04 01:10:13 +05:30
2024-09-30 18:13:39 +05:30
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-10-04 01:10:13 +05:30
result , err := aH . reader . GetListResultV3 ( r . Context ( ) , chq . Query )
2024-09-30 18:13:39 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2024-10-04 01:10:13 +05:30
RespondError ( w , apiErrObj , err )
2024-09-30 18:13:39 +05:30
return
}
2024-10-04 01:10:13 +05:30
var entries [ ] mq . OnboardingResponse
2024-09-30 18:13:39 +05:30
for _ , result := range result {
2024-10-04 01:10:13 +05:30
for key , value := range result . Data {
var message , attribute , status string
intValue := int ( * value . ( * uint8 ) )
if key == "entries" {
attribute = "telemetry ingestion"
if intValue != 0 {
entries = nil
entry := mq . OnboardingResponse {
Attribute : attribute ,
Message : "No data available in the given time range" ,
Status : "0" ,
}
entries = append ( entries , entry )
break
} else {
status = "1"
}
} else if key == "queue" {
attribute = "messaging.system"
if intValue != 0 {
status = "0"
message = "messaging.system attribute is not present or not equal to kafka in your spans"
} else {
status = "1"
}
} else if key == "kind" {
attribute = "kind"
if intValue != 0 {
status = "0"
2024-10-04 01:44:52 +05:30
message = "check if your consumer spans has kind=5 as attribute"
2024-10-04 01:10:13 +05:30
} else {
status = "1"
}
} else if key == "destination" {
attribute = "messaging.destination.name"
if intValue != 0 {
status = "0"
message = "messaging.destination.name attribute is not present in your spans"
} else {
status = "1"
}
} else if key == "partition" {
attribute = "messaging.destination.partition.id"
if intValue != 0 {
status = "0"
message = "messaging.destination.partition.id attribute is not present in your spans"
} else {
status = "1"
}
} else if key == "svc" {
attribute = "service_name"
if intValue != 0 {
status = "0"
message = "service_name attribute is not present in your spans"
} else {
status = "1"
}
} else if key == "cgroup" {
attribute = "messaging.kafka.consumer.group"
if intValue != 0 {
status = "0"
message = "messaging.kafka.consumer.group attribute is not present in your spans"
} else {
status = "1"
}
} else if key == "bodysize" {
attribute = "messaging.message.body.size"
if intValue != 0 {
status = "0"
message = "messaging.message.body.size attribute is not present in your spans"
} else {
status = "1"
}
} else if key == "clientid" {
attribute = "messaging.client_id"
if intValue != 0 {
status = "0"
message = "messaging.client_id attribute is not present in your spans"
} else {
status = "1"
}
} else if key == "instanceid" {
attribute = "service.instance.id"
if intValue != 0 {
status = "0"
message = "service.instance.id attribute is not present in your spans"
} else {
status = "1"
2024-09-30 18:13:39 +05:30
}
}
2024-10-04 01:10:13 +05:30
entry := mq . OnboardingResponse {
Attribute : attribute ,
Message : message ,
Status : status ,
}
entries = append ( entries , entry )
2024-09-30 18:13:39 +05:30
}
}
2024-10-04 01:44:52 +05:30
sort . Slice ( entries , func ( i , j int ) bool {
return entries [ i ] . Attribute < entries [ j ] . Attribute
} )
2024-10-04 01:10:13 +05:30
aH . Respond ( w , entries )
2024-09-30 18:13:39 +05:30
}
func ( aH * APIHandler ) onboardKafka (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-10-30 23:13:56 +05:30
queryRangeParams , err := mq . BuildBuilderQueriesKafkaOnboarding ( messagingQueue )
2024-10-04 01:10:13 +05:30
2024-09-30 18:13:39 +05:30
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-10-30 23:13:56 +05:30
results , errQueriesByName , err := aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
2024-09-30 18:13:39 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2024-10-30 23:13:56 +05:30
RespondError ( w , apiErrObj , errQueriesByName )
2024-09-30 18:13:39 +05:30
return
}
2024-10-04 01:10:13 +05:30
var entries [ ] mq . OnboardingResponse
2024-09-30 18:13:39 +05:30
2024-10-30 23:13:56 +05:30
var fetchLatencyState , consumerLagState bool
2024-10-04 01:10:13 +05:30
2024-10-30 23:13:56 +05:30
for _ , result := range results {
for _ , series := range result . Series {
for _ , point := range series . Points {
pointValue := point . Value
if pointValue > 0 {
if result . QueryName == "fetch_latency" {
fetchLatencyState = true
break
}
if result . QueryName == "consumer_lag" {
consumerLagState = true
break
2024-10-04 01:10:13 +05:30
}
2024-09-30 18:13:39 +05:30
}
2024-10-04 01:10:13 +05:30
}
2024-09-30 18:13:39 +05:30
}
}
2024-10-30 23:13:56 +05:30
if ! fetchLatencyState && ! consumerLagState {
entries = append ( entries , mq . OnboardingResponse {
Attribute : "telemetry ingestion" ,
Message : "No data available in the given time range" ,
Status : "0" ,
} )
}
if ! fetchLatencyState {
entries = append ( entries , mq . OnboardingResponse {
Attribute : "kafka_consumer_fetch_latency_avg" ,
Message : "Metric kafka_consumer_fetch_latency_avg is not present in the given time range." ,
Status : "0" ,
} )
} else {
entries = append ( entries , mq . OnboardingResponse {
Attribute : "kafka_consumer_fetch_latency_avg" ,
Status : "1" ,
} )
}
if ! consumerLagState {
entries = append ( entries , mq . OnboardingResponse {
Attribute : "kafka_consumer_group_lag" ,
Message : "Metric kafka_consumer_group_lag is not present in the given time range." ,
Status : "0" ,
} )
} else {
entries = append ( entries , mq . OnboardingResponse {
Attribute : "kafka_consumer_group_lag" ,
Status : "1" ,
} )
}
2024-10-04 01:44:52 +05:30
2024-10-04 01:10:13 +05:30
aH . Respond ( w , entries )
2024-09-30 18:13:39 +05:30
}
2024-08-07 13:51:00 +05:30
func ( aH * APIHandler ) getNetworkData (
w http . ResponseWriter , r * http . Request ,
) {
2024-08-27 18:27:44 +05:30
attributeCache := & mq . Clients {
Hash : make ( map [ string ] struct { } ) ,
}
2024-08-07 13:51:00 +05:30
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-10-17 19:44:42 +05:30
queryRangeParams , err := mq . BuildQRParamsWithCache ( messagingQueue , "throughput" , attributeCache )
2024-08-07 13:51:00 +05:30
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
2024-08-27 18:27:44 +05:30
var errQueriesByName map [ string ] error
2024-08-07 13:51:00 +05:30
2024-09-13 16:43:56 +05:30
result , errQueriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
2024-08-07 13:51:00 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2024-08-27 18:27:44 +05:30
RespondError ( w , apiErrObj , errQueriesByName )
2024-08-07 13:51:00 +05:30
return
}
for _ , res := range result {
2024-08-26 20:28:11 +05:30
for _ , series := range res . Series {
clientID , clientIDOk := series . Labels [ "client_id" ]
serviceInstanceID , serviceInstanceIDOk := series . Labels [ "service_instance_id" ]
serviceName , serviceNameOk := series . Labels [ "service_name" ]
2024-10-17 19:44:42 +05:30
params := [ ] string { clientID , serviceInstanceID , serviceName }
hashKey := uniqueIdentifier ( params , "#" )
2024-08-27 18:27:44 +05:30
_ , ok := attributeCache . Hash [ hashKey ]
if clientIDOk && serviceInstanceIDOk && serviceNameOk && ! ok {
attributeCache . Hash [ hashKey ] = struct { } { }
attributeCache . ClientID = append ( attributeCache . ClientID , clientID )
attributeCache . ServiceInstanceID = append ( attributeCache . ServiceInstanceID , serviceInstanceID )
attributeCache . ServiceName = append ( attributeCache . ServiceName , serviceName )
2024-08-07 13:51:00 +05:30
}
}
}
2024-10-17 19:44:42 +05:30
queryRangeParams , err = mq . BuildQRParamsWithCache ( messagingQueue , "fetch-latency" , attributeCache )
2024-08-07 13:51:00 +05:30
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-09-13 16:43:56 +05:30
resultFetchLatency , errQueriesByNameFetchLatency , err := aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
2024-08-07 13:51:00 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2024-08-27 18:27:44 +05:30
RespondError ( w , apiErrObj , errQueriesByNameFetchLatency )
2024-08-07 13:51:00 +05:30
return
}
2024-08-27 18:27:44 +05:30
latencyColumn := & v3 . Result { QueryName : "latency" }
2024-08-26 20:28:11 +05:30
var latencySeries [ ] * v3 . Series
for _ , res := range resultFetchLatency {
for _ , series := range res . Series {
2024-08-27 18:27:44 +05:30
clientID , clientIDOk := series . Labels [ "client_id" ]
serviceInstanceID , serviceInstanceIDOk := series . Labels [ "service_instance_id" ]
serviceName , serviceNameOk := series . Labels [ "service_name" ]
2024-10-17 19:44:42 +05:30
params := [ ] string { clientID , serviceInstanceID , serviceName }
hashKey := uniqueIdentifier ( params , "#" )
2024-08-27 18:27:44 +05:30
_ , ok := attributeCache . Hash [ hashKey ]
if clientIDOk && serviceInstanceIDOk && serviceNameOk && ok {
latencySeries = append ( latencySeries , series )
}
2024-08-26 20:28:11 +05:30
}
}
2024-08-27 18:27:44 +05:30
latencyColumn . Series = latencySeries
result = append ( result , latencyColumn )
2024-08-26 20:28:11 +05:30
resultFetchLatency = postprocess . TransformToTableForBuilderQueries ( result , queryRangeParams )
2024-08-07 13:51:00 +05:30
resp := v3 . QueryRangeResponse {
2024-08-26 20:28:11 +05:30
Result : resultFetchLatency ,
2024-08-07 13:51:00 +05:30
}
aH . Respond ( w , resp )
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) getProducerData (
w http . ResponseWriter , r * http . Request ,
) {
// parse the query params to retrieve the messaging queue struct
2024-07-26 15:23:31 +05:30
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
2024-07-26 11:50:02 +05:30
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
queryRangeParams , err := mq . BuildQueryRangeParams ( messagingQueue , "producer" )
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
2024-09-13 16:43:56 +05:30
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
2024-07-26 11:50:02 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
result = postprocess . TransformToTableForClickHouseQueries ( result )
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
func ( aH * APIHandler ) getConsumerData (
w http . ResponseWriter , r * http . Request ,
) {
2024-07-26 15:23:31 +05:30
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
2024-07-26 11:50:02 +05:30
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
queryRangeParams , err := mq . BuildQueryRangeParams ( messagingQueue , "consumer" )
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
2024-09-13 16:43:56 +05:30
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
2024-07-26 11:50:02 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
result = postprocess . TransformToTableForClickHouseQueries ( result )
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
2024-10-17 19:44:42 +05:30
// s1
func ( aH * APIHandler ) getPartitionOverviewLatencyData (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-10-25 14:06:54 +05:30
queryRangeParams , err := mq . BuildQueryRangeParams ( messagingQueue , "producer-topic-throughput" )
2024-10-17 19:44:42 +05:30
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
result = postprocess . TransformToTableForClickHouseQueries ( result )
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
// s1
func ( aH * APIHandler ) getConsumerPartitionLatencyData (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
queryRangeParams , err := mq . BuildQueryRangeParams ( messagingQueue , "consumer_partition_latency" )
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
result = postprocess . TransformToTableForClickHouseQueries ( result )
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
// s3 p overview
// fetch traces
// cache attributes
// fetch byte rate metrics
func ( aH * APIHandler ) getProducerThroughputOverview (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
attributeCache := & mq . Clients {
Hash : make ( map [ string ] struct { } ) ,
}
2024-12-09 20:48:41 +05:30
producerQueryRangeParams , err := mq . BuildQRParamsWithCache ( messagingQueue , "producer-throughput-overview" , attributeCache )
2024-10-17 19:44:42 +05:30
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
2024-12-09 20:48:41 +05:30
if err := validateQueryRangeParamsV3 ( producerQueryRangeParams ) ; err != nil {
2024-10-17 19:44:42 +05:30
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
2024-12-09 20:48:41 +05:30
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , producerQueryRangeParams )
2024-10-17 19:44:42 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
for _ , res := range result {
2024-12-09 20:48:41 +05:30
for _ , series := range res . Series {
serviceName , serviceNameOk := series . Labels [ "service_name" ]
topicName , topicNameOk := series . Labels [ "topic" ]
params := [ ] string { serviceName , topicName }
2024-10-17 19:44:42 +05:30
hashKey := uniqueIdentifier ( params , "#" )
_ , ok := attributeCache . Hash [ hashKey ]
if topicNameOk && serviceNameOk && ! ok {
attributeCache . Hash [ hashKey ] = struct { } { }
2024-12-09 20:48:41 +05:30
attributeCache . TopicName = append ( attributeCache . TopicName , topicName )
attributeCache . ServiceName = append ( attributeCache . ServiceName , serviceName )
2024-10-17 19:44:42 +05:30
}
}
}
2024-12-09 20:48:41 +05:30
queryRangeParams , err := mq . BuildQRParamsWithCache ( messagingQueue , "producer-throughput-overview-byte-rate" , attributeCache )
2024-10-17 19:44:42 +05:30
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
resultFetchLatency , errQueriesByNameFetchLatency , err := aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQueriesByNameFetchLatency )
return
}
2024-12-09 20:48:41 +05:30
byteRateColumn := & v3 . Result { QueryName : "byte_rate" }
var byteRateSeries [ ] * v3 . Series
2024-10-17 19:44:42 +05:30
for _ , res := range resultFetchLatency {
2024-12-09 20:48:41 +05:30
for _ , series := range res . Series {
topic , topicOk := series . Labels [ "topic" ]
serviceName , serviceNameOk := series . Labels [ "service_name" ]
params := [ ] string { serviceName , topic }
2024-10-17 19:44:42 +05:30
hashKey := uniqueIdentifier ( params , "#" )
_ , ok := attributeCache . Hash [ hashKey ]
if topicOk && serviceNameOk && ok {
2024-12-09 20:48:41 +05:30
byteRateSeries = append ( byteRateSeries , series )
2024-10-17 19:44:42 +05:30
}
}
}
2024-12-09 20:48:41 +05:30
byteRateColumn . Series = byteRateSeries
var latencyColumnResult [ ] * v3 . Result
latencyColumnResult = append ( latencyColumnResult , byteRateColumn )
2024-10-17 19:44:42 +05:30
2024-12-09 20:48:41 +05:30
resultFetchLatency = postprocess . TransformToTableForBuilderQueries ( latencyColumnResult , queryRangeParams )
result = postprocess . TransformToTableForClickHouseQueries ( result )
result = append ( result , resultFetchLatency [ 0 ] )
2024-10-17 19:44:42 +05:30
resp := v3 . QueryRangeResponse {
2024-12-09 20:48:41 +05:30
Result : result ,
2024-10-17 19:44:42 +05:30
}
aH . Respond ( w , resp )
}
// s3 p details
func ( aH * APIHandler ) getProducerThroughputDetails (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
queryRangeParams , err := mq . BuildQueryRangeParams ( messagingQueue , "producer-throughput-details" )
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
result = postprocess . TransformToTableForClickHouseQueries ( result )
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
// s3 c overview
func ( aH * APIHandler ) getConsumerThroughputOverview (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
queryRangeParams , err := mq . BuildQueryRangeParams ( messagingQueue , "consumer-throughput-overview" )
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
result = postprocess . TransformToTableForClickHouseQueries ( result )
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
// s3 c details
func ( aH * APIHandler ) getConsumerThroughputDetails (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
queryRangeParams , err := mq . BuildQueryRangeParams ( messagingQueue , "consumer-throughput-details" )
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
result = postprocess . TransformToTableForClickHouseQueries ( result )
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
// s4
// needs logic to parse duration
// needs logic to get the percentage
// show 10 traces
func ( aH * APIHandler ) getProducerConsumerEval (
w http . ResponseWriter , r * http . Request ,
) {
messagingQueue , apiErr := ParseMessagingQueueBody ( r )
if apiErr != nil {
zap . L ( ) . Error ( apiErr . Err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
queryRangeParams , err := mq . BuildQueryRangeParams ( messagingQueue , "producer-consumer-eval" )
if err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
if err := validateQueryRangeParamsV3 ( queryRangeParams ) ; err != nil {
zap . L ( ) . Error ( err . Error ( ) )
RespondError ( w , apiErr , nil )
return
}
var result [ ] * v3 . Result
var errQuriesByName map [ string ] error
result , errQuriesByName , err = aH . querierV2 . QueryRange ( r . Context ( ) , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
2024-07-26 15:23:31 +05:30
// ParseMessagingQueueBody parse for messaging queue params
func ParseMessagingQueueBody ( r * http . Request ) ( * mq . MessagingQueue , * model . ApiError ) {
messagingQueue := new ( mq . MessagingQueue )
if err := json . NewDecoder ( r . Body ) . Decode ( messagingQueue ) ; err != nil {
2024-07-26 11:50:02 +05:30
return nil , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "cannot parse the request body: %v" , err ) }
}
return messagingQueue , nil
}
2024-07-29 09:51:18 +05:30
// Preferences
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) getUserPreference (
2024-07-29 09:51:18 +05:30
w http . ResponseWriter , r * http . Request ,
) {
preferenceId := mux . Vars ( r ) [ "preferenceId" ]
user := common . GetUserFromContext ( r . Context ( ) )
preference , apiErr := preferences . GetUserPreference (
r . Context ( ) , preferenceId , user . User . OrgId , user . User . Id ,
)
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , preference )
2024-07-29 09:51:18 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) updateUserPreference (
2024-07-29 09:51:18 +05:30
w http . ResponseWriter , r * http . Request ,
) {
preferenceId := mux . Vars ( r ) [ "preferenceId" ]
user := common . GetUserFromContext ( r . Context ( ) )
req := preferences . UpdatePreference { }
err := json . NewDecoder ( r . Body ) . Decode ( & req )
if err != nil {
RespondError ( w , model . BadRequest ( err ) , nil )
return
}
preference , apiErr := preferences . UpdateUserPreference ( r . Context ( ) , preferenceId , req . PreferenceValue , user . User . Id )
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , preference )
2024-07-29 09:51:18 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) getAllUserPreferences (
2024-07-29 09:51:18 +05:30
w http . ResponseWriter , r * http . Request ,
) {
user := common . GetUserFromContext ( r . Context ( ) )
preference , apiErr := preferences . GetAllUserPreferences (
r . Context ( ) , user . User . OrgId , user . User . Id ,
)
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , preference )
2024-07-29 09:51:18 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) getOrgPreference (
2024-07-29 09:51:18 +05:30
w http . ResponseWriter , r * http . Request ,
) {
preferenceId := mux . Vars ( r ) [ "preferenceId" ]
user := common . GetUserFromContext ( r . Context ( ) )
preference , apiErr := preferences . GetOrgPreference (
r . Context ( ) , preferenceId , user . User . OrgId ,
)
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , preference )
2024-07-29 09:51:18 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) updateOrgPreference (
2024-07-29 09:51:18 +05:30
w http . ResponseWriter , r * http . Request ,
) {
preferenceId := mux . Vars ( r ) [ "preferenceId" ]
req := preferences . UpdatePreference { }
user := common . GetUserFromContext ( r . Context ( ) )
err := json . NewDecoder ( r . Body ) . Decode ( & req )
if err != nil {
RespondError ( w , model . BadRequest ( err ) , nil )
return
}
preference , apiErr := preferences . UpdateOrgPreference ( r . Context ( ) , preferenceId , req . PreferenceValue , user . User . OrgId )
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , preference )
2024-07-29 09:51:18 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) getAllOrgPreferences (
2024-07-29 09:51:18 +05:30
w http . ResponseWriter , r * http . Request ,
) {
user := common . GetUserFromContext ( r . Context ( ) )
preference , apiErr := preferences . GetAllOrgPreferences (
r . Context ( ) , user . User . OrgId ,
)
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , preference )
2024-07-29 09:51:18 +05:30
}
2024-07-26 11:50:02 +05:30
// RegisterIntegrationRoutes Registers all Integrations
func ( aH * APIHandler ) RegisterIntegrationRoutes ( router * mux . Router , am * AuthMiddleware ) {
2024-03-02 10:11:51 +05:30
subRouter := router . PathPrefix ( "/api/v1/integrations" ) . Subrouter ( )
subRouter . HandleFunc (
2024-07-26 11:50:02 +05:30
"/install" , am . ViewAccess ( aH . InstallIntegration ) ,
2024-03-02 10:11:51 +05:30
) . Methods ( http . MethodPost )
subRouter . HandleFunc (
2024-07-26 11:50:02 +05:30
"/uninstall" , am . ViewAccess ( aH . UninstallIntegration ) ,
2024-03-02 10:11:51 +05:30
) . Methods ( http . MethodPost )
2024-03-05 15:23:56 +05:30
// Used for polling for status in v0
subRouter . HandleFunc (
2024-07-26 11:50:02 +05:30
"/{integrationId}/connection_status" , am . ViewAccess ( aH . GetIntegrationConnectionStatus ) ,
2024-03-05 15:23:56 +05:30
) . Methods ( http . MethodGet )
2024-03-02 10:11:51 +05:30
subRouter . HandleFunc (
2024-07-26 11:50:02 +05:30
"/{integrationId}" , am . ViewAccess ( aH . GetIntegration ) ,
2024-03-02 10:11:51 +05:30
) . Methods ( http . MethodGet )
subRouter . HandleFunc (
2024-07-26 11:50:02 +05:30
"" , am . ViewAccess ( aH . ListIntegrations ) ,
2024-03-02 10:11:51 +05:30
) . Methods ( http . MethodGet )
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) ListIntegrations (
2024-03-02 10:11:51 +05:30
w http . ResponseWriter , r * http . Request ,
) {
params := map [ string ] string { }
for k , values := range r . URL . Query ( ) {
params [ k ] = values [ 0 ]
}
2024-07-26 11:50:02 +05:30
resp , apiErr := aH . IntegrationsController . ListIntegrations (
2024-03-02 10:11:51 +05:30
r . Context ( ) , params ,
)
if apiErr != nil {
RespondError ( w , apiErr , "Failed to fetch integrations" )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , resp )
2024-03-02 10:11:51 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) GetIntegration (
2024-03-02 10:11:51 +05:30
w http . ResponseWriter , r * http . Request ,
) {
integrationId := mux . Vars ( r ) [ "integrationId" ]
2024-07-26 11:50:02 +05:30
integration , apiErr := aH . IntegrationsController . GetIntegration (
2024-03-02 10:11:51 +05:30
r . Context ( ) , integrationId ,
)
if apiErr != nil {
RespondError ( w , apiErr , "Failed to fetch integration details" )
return
}
2024-03-05 15:23:56 +05:30
2024-07-26 11:50:02 +05:30
aH . Respond ( w , integration )
2024-03-05 15:23:56 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) GetIntegrationConnectionStatus (
2024-03-05 15:23:56 +05:30
w http . ResponseWriter , r * http . Request ,
) {
integrationId := mux . Vars ( r ) [ "integrationId" ]
2024-07-26 11:50:02 +05:30
isInstalled , apiErr := aH . IntegrationsController . IsIntegrationInstalled (
2024-03-18 10:01:53 +05:30
r . Context ( ) , integrationId ,
)
if apiErr != nil {
RespondError ( w , apiErr , "failed to check if integration is installed" )
return
}
// Do not spend resources calculating connection status unless installed.
if ! isInstalled {
2024-07-26 11:50:02 +05:30
aH . Respond ( w , & integrations . IntegrationConnectionStatus { } )
2024-03-18 10:01:53 +05:30
return
}
2024-07-26 11:50:02 +05:30
connectionTests , apiErr := aH . IntegrationsController . GetIntegrationConnectionTests (
2024-03-05 15:23:56 +05:30
r . Context ( ) , integrationId ,
)
if apiErr != nil {
2024-03-18 10:01:53 +05:30
RespondError ( w , apiErr , "failed to fetch integration connection tests" )
2024-03-05 15:23:56 +05:30
return
}
2024-03-06 11:06:04 +05:30
lookbackSecondsStr := r . URL . Query ( ) . Get ( "lookback_seconds" )
lookbackSeconds , err := strconv . ParseInt ( lookbackSecondsStr , 10 , 64 )
if err != nil {
lookbackSeconds = 15 * 60
}
2024-07-26 11:50:02 +05:30
connectionStatus , apiErr := aH . calculateConnectionStatus (
2024-03-06 11:06:04 +05:30
r . Context ( ) , connectionTests , lookbackSeconds ,
2024-03-05 15:23:56 +05:30
)
if apiErr != nil {
RespondError ( w , apiErr , "Failed to calculate integration connection status" )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , connectionStatus )
2024-03-05 15:23:56 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) calculateConnectionStatus (
2024-03-05 15:23:56 +05:30
ctx context . Context ,
connectionTests * integrations . IntegrationConnectionTests ,
2024-03-06 11:06:04 +05:30
lookbackSeconds int64 ,
2024-03-05 15:23:56 +05:30
) ( * integrations . IntegrationConnectionStatus , * model . ApiError ) {
2024-03-18 10:01:53 +05:30
// Calculate connection status for signals in parallel
2024-03-05 15:23:56 +05:30
result := & integrations . IntegrationConnectionStatus { }
2024-03-18 10:01:53 +05:30
errors := [ ] * model . ApiError { }
var resultLock sync . Mutex
2024-03-05 15:23:56 +05:30
2024-03-18 10:01:53 +05:30
var wg sync . WaitGroup
// Calculate logs connection status
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
2024-07-26 11:50:02 +05:30
logsConnStatus , apiErr := aH . calculateLogsConnectionStatus (
2024-03-18 10:01:53 +05:30
ctx , connectionTests . Logs , lookbackSeconds ,
2024-03-05 15:23:56 +05:30
)
2024-03-18 10:01:53 +05:30
resultLock . Lock ( )
defer resultLock . Unlock ( )
if apiErr != nil {
errors = append ( errors , apiErr )
} else {
result . Logs = logsConnStatus
}
} ( )
// Calculate metrics connection status
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
if connectionTests . Metrics == nil || len ( connectionTests . Metrics ) < 1 {
return
2024-03-05 15:23:56 +05:30
}
2024-07-26 11:50:02 +05:30
statusForLastReceivedMetric , apiErr := aH . reader . GetLatestReceivedMetric (
2024-03-18 10:01:53 +05:30
ctx , connectionTests . Metrics ,
)
resultLock . Lock ( )
defer resultLock . Unlock ( )
if apiErr != nil {
errors = append ( errors , apiErr )
} else if statusForLastReceivedMetric != nil {
2024-03-05 15:23:56 +05:30
resourceSummaryParts := [ ] string { }
2024-03-18 10:01:53 +05:30
for k , v := range statusForLastReceivedMetric . LastReceivedLabels {
2024-03-18 18:20:12 +05:30
interestingLabels := [ ] string {
"container_name" , "host_name" , "node_name" ,
"pod_name" , "deployment_name" , "cluster_name" ,
"namespace_name" , "job_name" , "service_name" ,
}
isInterestingKey := ! strings . HasPrefix ( k , "_" ) && slices . ContainsFunc (
interestingLabels , func ( l string ) bool { return strings . Contains ( k , l ) } ,
)
if isInterestingKey {
resourceSummaryParts = append ( resourceSummaryParts , fmt . Sprintf (
"%s=%s" , k , v ,
) )
}
2024-03-05 15:23:56 +05:30
}
2024-03-18 10:01:53 +05:30
result . Metrics = & integrations . SignalConnectionStatus {
LastReceivedFrom : strings . Join ( resourceSummaryParts , ", " ) ,
LastReceivedTsMillis : statusForLastReceivedMetric . LastReceivedTsMillis ,
2024-03-05 15:23:56 +05:30
}
}
2024-03-18 10:01:53 +05:30
} ( )
wg . Wait ( )
if len ( errors ) > 0 {
return nil , errors [ 0 ]
2024-03-05 15:23:56 +05:30
}
return result , nil
2024-03-02 10:11:51 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) calculateLogsConnectionStatus (
2024-03-18 10:01:53 +05:30
ctx context . Context ,
2024-05-15 14:36:52 +05:30
logsConnectionTest * integrations . LogsConnectionTest ,
2024-03-18 10:01:53 +05:30
lookbackSeconds int64 ,
) ( * integrations . SignalConnectionStatus , * model . ApiError ) {
if logsConnectionTest == nil {
return nil , nil
}
2024-05-15 14:36:52 +05:30
logsConnTestFilter := & v3 . FilterSet {
Operator : "AND" ,
Items : [ ] v3 . FilterItem {
{
Key : v3 . AttributeKey {
Key : logsConnectionTest . AttributeKey ,
DataType : v3 . AttributeKeyDataTypeString ,
Type : v3 . AttributeKeyTypeTag ,
} ,
Operator : "=" ,
Value : logsConnectionTest . AttributeValue ,
} ,
} ,
}
2024-03-18 10:01:53 +05:30
qrParams := & v3 . QueryRangeParamsV3 {
Start : time . Now ( ) . UnixMilli ( ) - ( lookbackSeconds * 1000 ) ,
End : time . Now ( ) . UnixMilli ( ) ,
CompositeQuery : & v3 . CompositeQuery {
PanelType : v3 . PanelTypeList ,
QueryType : v3 . QueryTypeBuilder ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
PageSize : 1 ,
2024-05-15 14:36:52 +05:30
Filters : logsConnTestFilter ,
2024-03-18 10:01:53 +05:30
QueryName : "A" ,
DataSource : v3 . DataSourceLogs ,
Expression : "A" ,
AggregateOperator : v3 . AggregateOperatorNoOp ,
} ,
} ,
} ,
}
2024-07-26 11:50:02 +05:30
queryRes , _ , err := aH . querier . QueryRange (
2024-09-13 16:43:56 +05:30
ctx , qrParams ,
2024-03-18 10:01:53 +05:30
)
if err != nil {
return nil , model . InternalError ( fmt . Errorf (
"could not query for integration connection status: %w" , err ,
) )
}
if len ( queryRes ) > 0 && queryRes [ 0 ] . List != nil && len ( queryRes [ 0 ] . List ) > 0 {
lastLog := queryRes [ 0 ] . List [ 0 ]
resourceSummaryParts := [ ] string { }
lastLogResourceAttribs := lastLog . Data [ "resources_string" ]
if lastLogResourceAttribs != nil {
resourceAttribs , ok := lastLogResourceAttribs . ( * map [ string ] string )
if ! ok {
return nil , model . InternalError ( fmt . Errorf (
"could not cast log resource attribs" ,
) )
}
for k , v := range * resourceAttribs {
resourceSummaryParts = append ( resourceSummaryParts , fmt . Sprintf (
"%s=%s" , k , v ,
) )
}
}
lastLogResourceSummary := strings . Join ( resourceSummaryParts , ", " )
return & integrations . SignalConnectionStatus {
LastReceivedTsMillis : lastLog . Timestamp . UnixMilli ( ) ,
LastReceivedFrom : lastLogResourceSummary ,
} , nil
}
return nil , nil
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) InstallIntegration (
2024-03-02 10:11:51 +05:30
w http . ResponseWriter , r * http . Request ,
) {
req := integrations . InstallIntegrationRequest { }
err := json . NewDecoder ( r . Body ) . Decode ( & req )
if err != nil {
RespondError ( w , model . BadRequest ( err ) , nil )
return
}
2024-07-26 11:50:02 +05:30
integration , apiErr := aH . IntegrationsController . Install (
2024-03-02 10:11:51 +05:30
r . Context ( ) , & req ,
)
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , integration )
2024-03-02 10:11:51 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) UninstallIntegration (
2024-03-02 10:11:51 +05:30
w http . ResponseWriter , r * http . Request ,
) {
req := integrations . UninstallIntegrationRequest { }
err := json . NewDecoder ( r . Body ) . Decode ( & req )
if err != nil {
RespondError ( w , model . BadRequest ( err ) , nil )
return
}
2024-07-26 11:50:02 +05:30
apiErr := aH . IntegrationsController . Uninstall ( r . Context ( ) , & req )
2024-03-02 10:11:51 +05:30
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , map [ string ] interface { } { } )
2024-03-02 10:11:51 +05:30
}
2022-07-12 16:38:26 +05:30
// logs
2023-02-15 23:49:03 +05:30
func ( aH * APIHandler ) RegisterLogsRoutes ( router * mux . Router , am * AuthMiddleware ) {
2022-07-12 16:38:26 +05:30
subRouter := router . PathPrefix ( "/api/v1/logs" ) . Subrouter ( )
2023-02-15 23:49:03 +05:30
subRouter . HandleFunc ( "" , am . ViewAccess ( aH . getLogs ) ) . Methods ( http . MethodGet )
subRouter . HandleFunc ( "/tail" , am . ViewAccess ( aH . tailLogs ) ) . Methods ( http . MethodGet )
subRouter . HandleFunc ( "/fields" , am . ViewAccess ( aH . logFields ) ) . Methods ( http . MethodGet )
subRouter . HandleFunc ( "/fields" , am . EditAccess ( aH . logFieldUpdate ) ) . Methods ( http . MethodPost )
subRouter . HandleFunc ( "/aggregate" , am . ViewAccess ( aH . logAggregate ) ) . Methods ( http . MethodGet )
2023-07-31 21:34:42 +05:30
// log pipelines
2023-10-10 14:09:55 +05:30
subRouter . HandleFunc ( "/pipelines/preview" , am . ViewAccess ( aH . PreviewLogsPipelinesHandler ) ) . Methods ( http . MethodPost )
2023-09-02 20:01:03 +05:30
subRouter . HandleFunc ( "/pipelines/{version}" , am . ViewAccess ( aH . ListLogsPipelinesHandler ) ) . Methods ( http . MethodGet )
subRouter . HandleFunc ( "/pipelines" , am . EditAccess ( aH . CreateLogsPipeline ) ) . Methods ( http . MethodPost )
2022-07-12 16:38:26 +05:30
}
func ( aH * APIHandler ) logFields ( w http . ResponseWriter , r * http . Request ) {
2022-10-06 20:13:30 +05:30
fields , apiErr := aH . reader . GetLogFields ( r . Context ( ) )
2022-07-12 16:38:26 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to fetch fields from the DB" )
2022-07-12 16:38:26 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , fields )
2022-07-12 16:38:26 +05:30
}
func ( aH * APIHandler ) logFieldUpdate ( w http . ResponseWriter , r * http . Request ) {
field := model . UpdateField { }
if err := json . NewDecoder ( r . Body ) . Decode ( & field ) ; err != nil {
apiErr := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to decode payload" )
2022-07-12 16:38:26 +05:30
return
}
err := logs . ValidateUpdateFieldPayload ( & field )
if err != nil {
apiErr := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Incorrect payload" )
2022-07-12 16:38:26 +05:30
return
}
2022-10-06 20:13:30 +05:30
apiErr := aH . reader . UpdateLogField ( r . Context ( ) , & field )
2022-07-12 16:38:26 +05:30
if apiErr != nil {
2024-08-09 12:38:40 +08:00
RespondError ( w , apiErr , "Failed to update field in the DB" )
2022-07-12 16:38:26 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , field )
2022-07-12 16:38:26 +05:30
}
2022-07-13 15:42:13 +05:30
func ( aH * APIHandler ) getLogs ( w http . ResponseWriter , r * http . Request ) {
2022-07-18 18:55:52 +05:30
params , err := logs . ParseLogFilterParams ( r )
2022-07-13 15:42:13 +05:30
if err != nil {
apiErr := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Incorrect params" )
2022-07-13 15:42:13 +05:30
return
}
2022-10-06 20:13:30 +05:30
res , apiErr := aH . reader . GetLogs ( r . Context ( ) , params )
2022-07-13 15:42:13 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to fetch logs from the DB" )
2022-07-13 15:42:13 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , map [ string ] interface { } { "results" : res } )
2022-07-13 15:42:13 +05:30
}
2022-07-18 16:37:46 +05:30
func ( aH * APIHandler ) tailLogs ( w http . ResponseWriter , r * http . Request ) {
2022-07-18 18:55:52 +05:30
params , err := logs . ParseLogFilterParams ( r )
if err != nil {
apiErr := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Incorrect params" )
2022-07-18 18:55:52 +05:30
return
}
// create the client
2023-10-09 15:25:13 +05:30
client := & model . LogsTailClient { Name : r . RemoteAddr , Logs : make ( chan * model . SignozLog , 1000 ) , Done : make ( chan * bool ) , Error : make ( chan error ) , Filter : * params }
2022-10-06 20:13:30 +05:30
go aH . reader . TailLogs ( r . Context ( ) , client )
2022-07-18 16:37:46 +05:30
w . Header ( ) . Set ( "Connection" , "keep-alive" )
w . Header ( ) . Set ( "Content-Type" , "text/event-stream" )
w . Header ( ) . Set ( "Cache-Control" , "no-cache" )
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , "*" )
w . WriteHeader ( 200 )
flusher , ok := w . ( http . Flusher )
if ! ok {
err := model . ApiError { Typ : model . ErrorStreamingNotSupported , Err : nil }
2022-10-06 20:13:30 +05:30
RespondError ( w , & err , "streaming is not supported" )
2022-07-18 16:37:46 +05:30
return
}
2022-12-26 15:29:49 +05:30
// flush the headers
flusher . Flush ( )
2022-07-18 16:37:46 +05:30
2022-07-19 16:38:28 +05:30
for {
2022-07-18 16:37:46 +05:30
select {
2022-07-19 16:38:28 +05:30
case log := <- client . Logs :
2022-07-18 16:37:46 +05:30
var buf bytes . Buffer
enc := json . NewEncoder ( & buf )
2022-07-19 16:38:28 +05:30
enc . Encode ( log )
2022-07-18 16:37:46 +05:30
fmt . Fprintf ( w , "data: %v\n\n" , buf . String ( ) )
flusher . Flush ( )
case <- client . Done :
2024-03-27 00:07:29 +05:30
zap . L ( ) . Debug ( "done!" )
2022-07-18 16:37:46 +05:30
return
2022-07-27 15:58:58 +05:30
case err := <- client . Error :
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error occured" , zap . Error ( err ) )
2022-07-18 18:55:52 +05:30
return
2022-07-18 16:37:46 +05:30
}
}
}
2022-07-20 12:11:03 +05:30
func ( aH * APIHandler ) logAggregate ( w http . ResponseWriter , r * http . Request ) {
params , err := logs . ParseLogAggregateParams ( r )
if err != nil {
apiErr := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Incorrect params" )
2022-07-20 12:11:03 +05:30
return
}
2022-10-06 20:13:30 +05:30
res , apiErr := aH . reader . AggregateLogs ( r . Context ( ) , params )
2022-07-20 12:11:03 +05:30
if apiErr != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to fetch logs aggregate from the DB" )
2022-07-20 12:11:03 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , res )
2022-07-20 12:11:03 +05:30
}
2023-03-04 00:05:16 +05:30
2023-07-31 21:34:42 +05:30
const logPipelines = "log_pipelines"
func parseAgentConfigVersion ( r * http . Request ) ( int , * model . ApiError ) {
versionString := mux . Vars ( r ) [ "version" ]
if versionString == "latest" {
return - 1 , nil
}
version64 , err := strconv . ParseInt ( versionString , 0 , 8 )
if err != nil {
return 0 , model . BadRequestStr ( "invalid version number" )
}
if version64 <= 0 {
return 0 , model . BadRequestStr ( "invalid version number" )
}
return int ( version64 ) , nil
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) PreviewLogsPipelinesHandler ( w http . ResponseWriter , r * http . Request ) {
2023-10-10 14:09:55 +05:30
req := logparsingpipeline . PipelinesPreviewRequest { }
if err := json . NewDecoder ( r . Body ) . Decode ( & req ) ; err != nil {
RespondError ( w , model . BadRequest ( err ) , nil )
return
}
2024-07-26 11:50:02 +05:30
resultLogs , apiErr := aH . LogsParsingPipelineController . PreviewLogsPipelines (
2023-10-10 14:09:55 +05:30
r . Context ( ) , & req ,
)
if apiErr != nil {
RespondError ( w , apiErr , nil )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , resultLogs )
2023-10-10 14:09:55 +05:30
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) ListLogsPipelinesHandler ( w http . ResponseWriter , r * http . Request ) {
2023-07-31 21:34:42 +05:30
version , err := parseAgentConfigVersion ( r )
if err != nil {
2023-09-10 16:48:29 +05:30
RespondError ( w , model . WrapApiError ( err , "Failed to parse agent config version" ) , nil )
2023-07-31 21:34:42 +05:30
return
}
var payload * logparsingpipeline . PipelinesResponse
var apierr * model . ApiError
if version != - 1 {
2024-07-26 11:50:02 +05:30
payload , apierr = aH . listLogsPipelinesByVersion ( context . Background ( ) , version )
2023-07-31 21:34:42 +05:30
} else {
2024-07-26 11:50:02 +05:30
payload , apierr = aH . listLogsPipelines ( context . Background ( ) )
2023-07-31 21:34:42 +05:30
}
if apierr != nil {
RespondError ( w , apierr , payload )
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , payload )
2023-07-31 21:34:42 +05:30
}
// listLogsPipelines lists logs piplines for latest version
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) listLogsPipelines ( ctx context . Context ) (
2023-09-10 16:48:29 +05:30
* logparsingpipeline . PipelinesResponse , * model . ApiError ,
) {
2023-07-31 21:34:42 +05:30
// get lateset agent config
2024-03-11 14:15:11 +05:30
latestVersion := - 1
2023-07-31 21:34:42 +05:30
lastestConfig , err := agentConf . GetLatestVersion ( ctx , logPipelines )
2024-03-11 14:15:11 +05:30
if err != nil && err . Type ( ) != model . ErrorNotFound {
return nil , model . WrapApiError ( err , "failed to get latest agent config version" )
}
if lastestConfig != nil {
latestVersion = lastestConfig . Version
2023-07-31 21:34:42 +05:30
}
2024-07-26 11:50:02 +05:30
payload , err := aH . LogsParsingPipelineController . GetPipelinesByVersion ( ctx , latestVersion )
2023-07-31 21:34:42 +05:30
if err != nil {
2023-09-10 16:48:29 +05:30
return nil , model . WrapApiError ( err , "failed to get pipelines" )
2023-07-31 21:34:42 +05:30
}
// todo(Nitya): make a new API for history pagination
limit := 10
history , err := agentConf . GetConfigHistory ( ctx , logPipelines , limit )
if err != nil {
2023-09-10 16:48:29 +05:30
return nil , model . WrapApiError ( err , "failed to get config history" )
2023-07-31 21:34:42 +05:30
}
payload . History = history
return payload , nil
}
// listLogsPipelinesByVersion lists pipelines along with config version history
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) listLogsPipelinesByVersion ( ctx context . Context , version int ) (
2023-09-10 16:48:29 +05:30
* logparsingpipeline . PipelinesResponse , * model . ApiError ,
) {
2024-07-26 11:50:02 +05:30
payload , err := aH . LogsParsingPipelineController . GetPipelinesByVersion ( ctx , version )
2023-07-31 21:34:42 +05:30
if err != nil {
2023-09-10 16:48:29 +05:30
return nil , model . WrapApiError ( err , "failed to get pipelines by version" )
2023-07-31 21:34:42 +05:30
}
// todo(Nitya): make a new API for history pagination
limit := 10
history , err := agentConf . GetConfigHistory ( ctx , logPipelines , limit )
if err != nil {
2023-09-10 16:48:29 +05:30
return nil , model . WrapApiError ( err , "failed to retrieve agent config history" )
2023-07-31 21:34:42 +05:30
}
payload . History = history
return payload , nil
}
2024-07-26 11:50:02 +05:30
func ( aH * APIHandler ) CreateLogsPipeline ( w http . ResponseWriter , r * http . Request ) {
2023-07-31 21:34:42 +05:30
req := logparsingpipeline . PostablePipelines { }
if err := json . NewDecoder ( r . Body ) . Decode ( & req ) ; err != nil {
RespondError ( w , model . BadRequest ( err ) , nil )
return
}
2023-09-10 16:48:29 +05:30
createPipeline := func (
ctx context . Context ,
postable [ ] logparsingpipeline . PostablePipeline ,
) ( * logparsingpipeline . PipelinesResponse , * model . ApiError ) {
2023-07-31 21:34:42 +05:30
if len ( postable ) == 0 {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Warn ( "found no pipelines in the http request, this will delete all the pipelines" )
2023-07-31 21:34:42 +05:30
}
2024-12-18 10:42:14 +05:30
validationErr := aH . LogsParsingPipelineController . ValidatePipelines ( ctx , postable )
if validationErr != nil {
return nil , validationErr
2023-07-31 21:34:42 +05:30
}
2024-07-26 11:50:02 +05:30
return aH . LogsParsingPipelineController . ApplyPipelines ( ctx , postable )
2023-07-31 21:34:42 +05:30
}
2023-11-16 15:11:38 +05:30
res , err := createPipeline ( r . Context ( ) , req . Pipelines )
2023-07-31 21:34:42 +05:30
if err != nil {
2023-09-10 16:48:29 +05:30
RespondError ( w , err , nil )
2023-07-31 21:34:42 +05:30
return
}
2024-07-26 11:50:02 +05:30
aH . Respond ( w , res )
2023-07-31 21:34:42 +05:30
}
2023-08-25 09:22:46 +05:30
func ( aH * APIHandler ) getSavedViews ( w http . ResponseWriter , r * http . Request ) {
// get sourcePage, name, and category from the query params
sourcePage := r . URL . Query ( ) . Get ( "sourcePage" )
name := r . URL . Query ( ) . Get ( "name" )
category := r . URL . Query ( ) . Get ( "category" )
queries , err := explorer . GetViewsForFilters ( sourcePage , name , category )
2023-03-07 00:26:25 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , queries )
}
2023-08-25 09:22:46 +05:30
func ( aH * APIHandler ) createSavedViews ( w http . ResponseWriter , r * http . Request ) {
var view v3 . SavedView
err := json . NewDecoder ( r . Body ) . Decode ( & view )
2023-03-07 00:26:25 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
// validate the query
2023-08-25 09:22:46 +05:30
if err := view . Validate ( ) ; err != nil {
2023-03-07 00:26:25 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
2023-11-16 15:11:38 +05:30
uuid , err := explorer . CreateView ( r . Context ( ) , view )
2023-03-07 00:26:25 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , uuid )
}
2023-08-25 09:22:46 +05:30
func ( aH * APIHandler ) getSavedView ( w http . ResponseWriter , r * http . Request ) {
viewID := mux . Vars ( r ) [ "viewId" ]
view , err := explorer . GetView ( viewID )
2023-03-07 00:26:25 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
2023-08-25 09:22:46 +05:30
aH . Respond ( w , view )
2023-03-07 00:26:25 +05:30
}
2023-08-25 09:22:46 +05:30
func ( aH * APIHandler ) updateSavedView ( w http . ResponseWriter , r * http . Request ) {
viewID := mux . Vars ( r ) [ "viewId" ]
var view v3 . SavedView
err := json . NewDecoder ( r . Body ) . Decode ( & view )
2023-03-07 00:26:25 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
// validate the query
2023-08-25 09:22:46 +05:30
if err := view . Validate ( ) ; err != nil {
2023-03-07 00:26:25 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
2023-11-16 15:11:38 +05:30
err = explorer . UpdateView ( r . Context ( ) , viewID , view )
2023-03-07 00:26:25 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
2023-08-25 09:22:46 +05:30
aH . Respond ( w , view )
2023-03-07 00:26:25 +05:30
}
2023-08-25 09:22:46 +05:30
func ( aH * APIHandler ) deleteSavedView ( w http . ResponseWriter , r * http . Request ) {
2023-03-07 00:26:25 +05:30
2023-08-25 09:22:46 +05:30
viewID := mux . Vars ( r ) [ "viewId" ]
err := explorer . DeleteView ( viewID )
2023-03-07 00:26:25 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , nil )
}
2023-03-04 00:05:16 +05:30
func ( aH * APIHandler ) autocompleteAggregateAttributes ( w http . ResponseWriter , r * http . Request ) {
var response * v3 . AggregateAttributeResponse
req , err := parseAggregateAttributeRequest ( r )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
switch req . DataSource {
case v3 . DataSourceMetrics :
2024-10-10 17:01:44 +05:30
response , err = aH . reader . GetMetricAggregateAttributes ( r . Context ( ) , req , true )
2023-03-04 00:05:16 +05:30
case v3 . DataSourceLogs :
2023-04-06 13:32:24 +05:30
response , err = aH . reader . GetLogAggregateAttributes ( r . Context ( ) , req )
2023-03-04 00:05:16 +05:30
case v3 . DataSourceTraces :
2023-04-13 15:33:08 +05:30
response , err = aH . reader . GetTraceAggregateAttributes ( r . Context ( ) , req )
2023-03-04 00:05:16 +05:30
default :
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "invalid data source" ) } , nil )
return
}
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
aH . Respond ( w , response )
}
2023-03-10 11:22:34 +05:30
2024-08-08 09:27:41 +05:30
func ( aH * APIHandler ) getQueryBuilderSuggestions ( w http . ResponseWriter , r * http . Request ) {
req , err := parseQBFilterSuggestionsRequest ( r )
if err != nil {
RespondError ( w , err , nil )
return
}
if req . DataSource != v3 . DataSourceLogs {
// Support for traces and metrics might come later
RespondError ( w , model . BadRequest (
fmt . Errorf ( "suggestions not supported for %s" , req . DataSource ) ,
) , nil )
return
}
response , err := aH . reader . GetQBFilterSuggestionsForLogs ( r . Context ( ) , req )
if err != nil {
RespondError ( w , err , nil )
return
}
aH . Respond ( w , response )
}
2023-03-10 11:22:34 +05:30
func ( aH * APIHandler ) autoCompleteAttributeKeys ( w http . ResponseWriter , r * http . Request ) {
var response * v3 . FilterAttributeKeyResponse
req , err := parseFilterAttributeKeyRequest ( r )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
switch req . DataSource {
case v3 . DataSourceMetrics :
response , err = aH . reader . GetMetricAttributeKeys ( r . Context ( ) , req )
case v3 . DataSourceLogs :
2023-04-06 13:32:24 +05:30
response , err = aH . reader . GetLogAttributeKeys ( r . Context ( ) , req )
2023-03-10 11:22:34 +05:30
case v3 . DataSourceTraces :
2023-04-13 15:33:08 +05:30
response , err = aH . reader . GetTraceAttributeKeys ( r . Context ( ) , req )
2023-03-10 11:22:34 +05:30
default :
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "invalid data source" ) } , nil )
return
}
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
aH . Respond ( w , response )
}
func ( aH * APIHandler ) autoCompleteAttributeValues ( w http . ResponseWriter , r * http . Request ) {
var response * v3 . FilterAttributeValueResponse
req , err := parseFilterAttributeValueRequest ( r )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
switch req . DataSource {
case v3 . DataSourceMetrics :
response , err = aH . reader . GetMetricAttributeValues ( r . Context ( ) , req )
case v3 . DataSourceLogs :
2023-04-06 13:32:24 +05:30
response , err = aH . reader . GetLogAttributeValues ( r . Context ( ) , req )
2023-03-10 11:22:34 +05:30
case v3 . DataSourceTraces :
2023-04-13 15:33:08 +05:30
response , err = aH . reader . GetTraceAttributeValues ( r . Context ( ) , req )
2023-03-10 11:22:34 +05:30
default :
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "invalid data source" ) } , nil )
return
}
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
aH . Respond ( w , response )
}
2023-03-23 19:45:15 +05:30
2023-04-25 21:53:46 +05:30
func ( aH * APIHandler ) getSpanKeysV3 ( ctx context . Context , queryRangeParams * v3 . QueryRangeParamsV3 ) ( map [ string ] v3 . AttributeKey , error ) {
data := map [ string ] v3 . AttributeKey { }
for _ , query := range queryRangeParams . CompositeQuery . BuilderQueries {
if query . DataSource == v3 . DataSourceTraces {
spanKeys , err := aH . reader . GetSpanAttributeKeys ( ctx )
if err != nil {
return nil , err
}
2023-07-05 06:57:39 +05:30
// Add timestamp as a span key to allow ordering by timestamp
spanKeys [ "timestamp" ] = v3 . AttributeKey {
Key : "timestamp" ,
IsColumn : true ,
}
2023-04-25 21:53:46 +05:30
return spanKeys , nil
}
}
return data , nil
}
2023-12-11 17:46:08 +05:30
func ( aH * APIHandler ) QueryRangeV3Format ( w http . ResponseWriter , r * http . Request ) {
queryRangeParams , apiErrorObj := ParseQueryRangeParams ( r )
if apiErrorObj != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( apiErrorObj . Err . Error ( ) )
2023-12-11 17:46:08 +05:30
RespondError ( w , apiErrorObj , nil )
return
}
2024-06-25 13:42:40 +05:30
queryRangeParams . Version = "v3"
2023-12-11 17:46:08 +05:30
aH . Respond ( w , queryRangeParams )
}
2023-04-10 19:36:13 +05:30
func ( aH * APIHandler ) queryRangeV3 ( ctx context . Context , queryRangeParams * v3 . QueryRangeParamsV3 , w http . ResponseWriter , r * http . Request ) {
2023-03-23 19:45:15 +05:30
var result [ ] * v3 . Result
var err error
2024-05-15 18:52:01 +05:30
var errQuriesByName map [ string ] error
2023-09-17 10:40:45 +05:30
var spanKeys map [ string ] v3 . AttributeKey
if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeBuilder {
2023-06-09 17:07:45 +05:30
// check if any enrichment is required for logs if yes then enrich them
if logsv3 . EnrichmentRequired ( queryRangeParams ) {
2024-09-17 15:33:17 +05:30
logsFields , err := aH . reader . GetLogFields ( ctx )
2023-06-09 17:07:45 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
2024-09-17 15:33:17 +05:30
// get the fields if any logs query is present
fields := model . GetLogFieldsV3 ( ctx , queryRangeParams , logsFields )
2023-06-09 17:07:45 +05:30
logsv3 . Enrich ( queryRangeParams , fields )
2023-04-10 19:36:13 +05:30
}
2023-04-25 21:53:46 +05:30
spanKeys , err = aH . getSpanKeysV3 ( ctx , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
2024-11-22 12:00:29 +05:30
if aH . UseTraceNewSchema {
tracesV4 . Enrich ( queryRangeParams , spanKeys )
} else {
tracesV3 . Enrich ( queryRangeParams , spanKeys )
}
2023-03-23 19:45:15 +05:30
}
2024-07-31 16:00:57 +05:30
// WARN: Only works for AND operator in traces query
if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeBuilder {
// check if traceID is used as filter (with equal/similar operator) in traces query if yes add timestamp filter to queryRange params
isUsed , traceIDs := tracesV3 . TraceIdFilterUsedWithEqual ( queryRangeParams )
2024-09-13 16:43:56 +05:30
if isUsed && len ( traceIDs ) > 0 {
2024-07-31 16:00:57 +05:30
zap . L ( ) . Debug ( "traceID used as filter in traces query" )
// query signoz_spans table with traceID to get min and max timestamp
min , max , err := aH . reader . GetMinAndMaxTimestampForTraceID ( ctx , traceIDs )
if err == nil {
// add timestamp filter to queryRange params
tracesV3 . AddTimestampFilters ( min , max , queryRangeParams )
zap . L ( ) . Debug ( "post adding timestamp filter in traces query" , zap . Any ( "queryRangeParams" , queryRangeParams ) )
}
}
}
2024-08-14 19:53:36 +05:30
// Hook up query progress tracking if requested
queryIdHeader := r . Header . Get ( "X-SIGNOZ-QUERY-ID" )
if len ( queryIdHeader ) > 0 {
onQueryFinished , err := aH . reader . ReportQueryStartForProgressTracking ( queryIdHeader )
2024-09-17 12:38:53 +05:30
2024-08-14 19:53:36 +05:30
if err != nil {
zap . L ( ) . Error (
"couldn't report query start for progress tracking" ,
zap . String ( "queryId" , queryIdHeader ) , zap . Error ( err ) ,
)
2024-09-17 12:38:53 +05:30
2024-08-14 19:53:36 +05:30
} else {
2024-09-17 12:38:53 +05:30
// Adding queryId to the context signals clickhouse queries to report progress
2024-09-17 15:33:17 +05:30
//lint:ignore SA1029 ignore for now
2024-09-17 12:38:53 +05:30
ctx = context . WithValue ( ctx , "queryId" , queryIdHeader )
2024-08-14 19:53:36 +05:30
defer func ( ) {
go onQueryFinished ( )
} ( )
}
}
2024-09-13 16:43:56 +05:30
result , errQuriesByName , err = aH . querier . QueryRange ( ctx , queryRangeParams )
2023-09-17 10:40:45 +05:30
2023-03-23 19:45:15 +05:30
if err != nil {
2024-09-13 18:01:37 +05:30
queryErrors := map [ string ] string { }
for name , err := range errQuriesByName {
queryErrors [ fmt . Sprintf ( "Query-%s" , name ) ] = err . Error ( )
}
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , queryErrors )
2023-03-23 19:45:15 +05:30
return
}
2024-06-13 20:37:44 +05:30
postprocess . ApplyHavingClause ( result , queryRangeParams )
2024-05-24 12:11:34 +05:30
postprocess . ApplyMetricLimit ( result , queryRangeParams )
2023-07-13 14:22:30 +05:30
2024-03-11 16:45:06 +05:30
sendQueryResultEvents ( r , result , queryRangeParams )
2024-03-08 21:12:53 +05:30
// only adding applyFunctions instead of postProcess since experssion are
// are executed in clickhouse directly and we wanted to add support for timeshift
if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeBuilder {
2024-05-24 12:11:34 +05:30
postprocess . ApplyFunctions ( result , queryRangeParams )
2024-03-08 21:12:53 +05:30
}
2024-06-13 20:37:44 +05:30
if queryRangeParams . CompositeQuery . FillGaps {
postprocess . FillGaps ( result , queryRangeParams )
}
2024-06-25 13:42:40 +05:30
if queryRangeParams . CompositeQuery . PanelType == v3 . PanelTypeTable && queryRangeParams . FormatForWeb {
if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeClickHouseSQL {
result = postprocess . TransformToTableForClickHouseQueries ( result )
} else if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeBuilder {
result = postprocess . TransformToTableForBuilderQueries ( result , queryRangeParams )
}
}
2023-03-23 19:45:15 +05:30
resp := v3 . QueryRangeResponse {
Result : result ,
}
2023-11-29 09:10:30 +05:30
// This checks if the time for context to complete has exceeded.
2024-03-11 16:45:06 +05:30
// it adds flag to notify the user of incomplete response
2023-11-29 09:10:30 +05:30
select {
case <- ctx . Done ( ) :
resp . ContextTimeout = true
resp . ContextTimeoutMessage = "result might contain incomplete data due to context timeout, for custom timeout set the timeout header eg:- timeout:120"
default :
break
}
2023-03-23 19:45:15 +05:30
aH . Respond ( w , resp )
}
2024-03-11 16:45:06 +05:30
func sendQueryResultEvents ( r * http . Request , result [ ] * v3 . Result , queryRangeParams * v3 . QueryRangeParamsV3 ) {
referrer := r . Header . Get ( "Referer" )
dashboardMatched , err := regexp . MatchString ( ` /dashboard/[a-zA-Z0-9\-]+/(new|edit)(?:\?.*)?$ ` , referrer )
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error while matching the referrer" , zap . Error ( err ) )
2024-03-11 16:45:06 +05:30
}
alertMatched , err := regexp . MatchString ( ` /alerts/(new|edit)(?:\?.*)?$ ` , referrer )
if err != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error while matching the alert: " , zap . Error ( err ) )
2024-03-11 16:45:06 +05:30
}
if alertMatched || dashboardMatched {
if len ( result ) > 0 && ( len ( result [ 0 ] . Series ) > 0 || len ( result [ 0 ] . List ) > 0 ) {
userEmail , err := auth . GetEmailFromJwt ( r . Context ( ) )
if err == nil {
signozLogsUsed , signozMetricsUsed , signozTracesUsed := telemetry . GetInstance ( ) . CheckSigNozSignals ( queryRangeParams )
if signozLogsUsed || signozMetricsUsed || signozTracesUsed {
2024-03-20 19:59:28 +05:30
2024-03-11 16:45:06 +05:30
if dashboardMatched {
2024-03-20 19:59:28 +05:30
var dashboardID , widgetID string
var dashboardIDMatch , widgetIDMatch [ ] string
dashboardIDRegex , err := regexp . Compile ( ` /dashboard/([a-f0-9\-]+)/ ` )
if err == nil {
dashboardIDMatch = dashboardIDRegex . FindStringSubmatch ( referrer )
} else {
zap . S ( ) . Errorf ( "error while matching the dashboardIDRegex: %v" , err )
}
widgetIDRegex , err := regexp . Compile ( ` widgetId=([a-f0-9\-]+) ` )
if err == nil {
widgetIDMatch = widgetIDRegex . FindStringSubmatch ( referrer )
} else {
zap . S ( ) . Errorf ( "error while matching the widgetIDRegex: %v" , err )
}
if len ( dashboardIDMatch ) > 1 {
dashboardID = dashboardIDMatch [ 1 ]
}
if len ( widgetIDMatch ) > 1 {
widgetID = widgetIDMatch [ 1 ]
}
2024-03-11 16:45:06 +05:30
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_EVENT_SUCCESSFUL_DASHBOARD_PANEL_QUERY , map [ string ] interface { } {
"queryType" : queryRangeParams . CompositeQuery . QueryType ,
"panelType" : queryRangeParams . CompositeQuery . PanelType ,
"tracesUsed" : signozTracesUsed ,
"logsUsed" : signozLogsUsed ,
"metricsUsed" : signozMetricsUsed ,
2024-03-20 19:59:28 +05:30
"dashboardId" : dashboardID ,
"widgetId" : widgetID ,
2024-03-28 21:43:41 +05:30
} , userEmail , true , false )
2024-03-11 16:45:06 +05:30
}
if alertMatched {
2024-03-20 19:59:28 +05:30
var alertID string
var alertIDMatch [ ] string
alertIDRegex , err := regexp . Compile ( ` ruleId=(\d+) ` )
if err != nil {
zap . S ( ) . Errorf ( "error while matching the alertIDRegex: %v" , err )
} else {
alertIDMatch = alertIDRegex . FindStringSubmatch ( referrer )
}
if len ( alertIDMatch ) > 1 {
alertID = alertIDMatch [ 1 ]
}
2024-03-11 16:45:06 +05:30
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_EVENT_SUCCESSFUL_ALERT_QUERY , map [ string ] interface { } {
"queryType" : queryRangeParams . CompositeQuery . QueryType ,
"panelType" : queryRangeParams . CompositeQuery . PanelType ,
"tracesUsed" : signozTracesUsed ,
"logsUsed" : signozLogsUsed ,
"metricsUsed" : signozMetricsUsed ,
2024-03-20 19:59:28 +05:30
"alertId" : alertID ,
2024-03-28 21:43:41 +05:30
} , userEmail , true , false )
2024-03-11 16:45:06 +05:30
}
}
}
}
}
}
2023-03-23 19:45:15 +05:30
func ( aH * APIHandler ) QueryRangeV3 ( w http . ResponseWriter , r * http . Request ) {
queryRangeParams , apiErrorObj := ParseQueryRangeParams ( r )
if apiErrorObj != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error parsing metric query range params" , zap . Error ( apiErrorObj . Err ) )
2023-03-23 19:45:15 +05:30
RespondError ( w , apiErrorObj , nil )
return
}
2023-07-13 18:50:19 +05:30
// add temporality for each metric
2024-09-24 10:22:52 +05:30
temporalityErr := aH . PopulateTemporality ( r . Context ( ) , queryRangeParams )
2023-07-13 18:50:19 +05:30
if temporalityErr != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error while adding temporality for metrics" , zap . Error ( temporalityErr ) )
2023-07-13 18:50:19 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : temporalityErr } , nil )
return
}
2023-04-10 19:36:13 +05:30
aH . queryRangeV3 ( r . Context ( ) , queryRangeParams , w , r )
2023-03-23 19:45:15 +05:30
}
2023-07-13 14:22:30 +05:30
2024-08-14 19:53:36 +05:30
func ( aH * APIHandler ) GetQueryProgressUpdates ( w http . ResponseWriter , r * http . Request ) {
// Upgrade connection to websocket, sending back the requested protocol
// value for sec-websocket-protocol
//
// Since js websocket API doesn't allow setting headers, this header is often
// used for passing auth tokens. As per websocket spec the connection will only
// succeed if the requested `Sec-Websocket-Protocol` is sent back as a header
// in the upgrade response (signifying that the protocol is supported by the server).
upgradeResponseHeaders := http . Header { }
requestedProtocol := r . Header . Get ( "Sec-WebSocket-Protocol" )
if len ( requestedProtocol ) > 0 {
upgradeResponseHeaders . Add ( "Sec-WebSocket-Protocol" , requestedProtocol )
}
c , err := aH . Upgrader . Upgrade ( w , r , upgradeResponseHeaders )
if err != nil {
RespondError ( w , model . InternalError ( fmt . Errorf (
"couldn't upgrade connection: %w" , err ,
) ) , nil )
return
}
defer c . Close ( )
// Websocket upgrade complete. Subscribe to query progress and send updates to client
//
// Note: we handle any subscription problems (queryId query param missing or query already complete etc)
// after the websocket connection upgrade by closing the channel.
// The other option would be to handle the errors before websocket upgrade by sending an
// error response instead of the upgrade response, but that leads to a generic websocket
// connection failure on the client.
queryId := r . URL . Query ( ) . Get ( "q" )
progressCh , unsubscribe , apiErr := aH . reader . SubscribeToQueryProgress ( queryId )
if apiErr != nil {
// Shouldn't happen unless query progress requested after query finished
zap . L ( ) . Warn (
"couldn't subscribe to query progress" ,
2024-08-23 17:37:56 +05:30
zap . String ( "queryId" , queryId ) , zap . Any ( "error" , apiErr ) ,
2024-08-14 19:53:36 +05:30
)
return
}
defer func ( ) { go unsubscribe ( ) } ( )
for queryProgress := range progressCh {
msg , err := json . Marshal ( queryProgress )
if err != nil {
zap . L ( ) . Error (
"failed to serialize progress message" ,
zap . String ( "queryId" , queryId ) , zap . Any ( "progress" , queryProgress ) , zap . Error ( err ) ,
)
continue
}
err = c . WriteMessage ( websocket . TextMessage , msg )
if err != nil {
zap . L ( ) . Error (
"failed to write progress msg to websocket" ,
zap . String ( "queryId" , queryId ) , zap . String ( "msg" , string ( msg ) ) , zap . Error ( err ) ,
)
break
} else {
zap . L ( ) . Debug (
"wrote progress msg to websocket" ,
zap . String ( "queryId" , queryId ) , zap . String ( "msg" , string ( msg ) ) , zap . Error ( err ) ,
)
}
}
}
2024-09-13 17:04:22 +05:30
func ( aH * APIHandler ) liveTailLogsV2 ( w http . ResponseWriter , r * http . Request ) {
// get the param from url and add it to body
stringReader := strings . NewReader ( r . URL . Query ( ) . Get ( "q" ) )
r . Body = io . NopCloser ( stringReader )
queryRangeParams , apiErrorObj := ParseQueryRangeParams ( r )
if apiErrorObj != nil {
zap . L ( ) . Error ( apiErrorObj . Err . Error ( ) )
RespondError ( w , apiErrorObj , nil )
return
}
var err error
var queryString string
switch queryRangeParams . CompositeQuery . QueryType {
case v3 . QueryTypeBuilder :
// check if any enrichment is required for logs if yes then enrich them
if logsv3 . EnrichmentRequired ( queryRangeParams ) {
// get the fields if any logs query is present
2024-09-17 15:33:17 +05:30
logsFields , err := aH . reader . GetLogFields ( r . Context ( ) )
2024-09-13 17:04:22 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , nil )
return
}
2024-09-17 15:33:17 +05:30
fields := model . GetLogFieldsV3 ( r . Context ( ) , queryRangeParams , logsFields )
2024-09-13 17:04:22 +05:30
logsv3 . Enrich ( queryRangeParams , fields )
}
queryString , err = aH . queryBuilder . PrepareLiveTailQuery ( queryRangeParams )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
default :
err = fmt . Errorf ( "invalid query type" )
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
w . Header ( ) . Set ( "Connection" , "keep-alive" )
w . Header ( ) . Set ( "Content-Type" , "text/event-stream" )
w . Header ( ) . Set ( "Cache-Control" , "no-cache" )
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , "*" )
w . WriteHeader ( 200 )
flusher , ok := w . ( http . Flusher )
if ! ok {
err := model . ApiError { Typ : model . ErrorStreamingNotSupported , Err : nil }
RespondError ( w , & err , "streaming is not supported" )
return
}
// flush the headers
flusher . Flush ( )
// create the client
2024-09-13 18:10:49 +05:30
client := & model . LogsLiveTailClientV2 { Name : r . RemoteAddr , Logs : make ( chan * model . SignozLogV2 , 1000 ) , Done : make ( chan * bool ) , Error : make ( chan error ) }
2024-09-13 17:04:22 +05:30
go aH . reader . LiveTailLogsV4 ( r . Context ( ) , queryString , uint64 ( queryRangeParams . Start ) , "" , client )
for {
select {
case log := <- client . Logs :
var buf bytes . Buffer
enc := json . NewEncoder ( & buf )
enc . Encode ( log )
fmt . Fprintf ( w , "data: %v\n\n" , buf . String ( ) )
flusher . Flush ( )
case <- client . Done :
zap . L ( ) . Debug ( "done!" )
return
case err := <- client . Error :
zap . L ( ) . Error ( "error occurred" , zap . Error ( err ) )
fmt . Fprintf ( w , "event: error\ndata: %v\n\n" , err . Error ( ) )
flusher . Flush ( )
return
}
}
}
2023-07-20 17:53:55 +05:30
func ( aH * APIHandler ) liveTailLogs ( w http . ResponseWriter , r * http . Request ) {
2024-09-13 17:04:22 +05:30
if aH . UseLogsNewSchema {
aH . liveTailLogsV2 ( w , r )
return
}
2023-07-20 17:53:55 +05:30
2023-08-11 21:54:44 +05:30
// get the param from url and add it to body
stringReader := strings . NewReader ( r . URL . Query ( ) . Get ( "q" ) )
r . Body = io . NopCloser ( stringReader )
2023-07-20 17:53:55 +05:30
queryRangeParams , apiErrorObj := ParseQueryRangeParams ( r )
if apiErrorObj != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( apiErrorObj . Err . Error ( ) )
2023-07-20 17:53:55 +05:30
RespondError ( w , apiErrorObj , nil )
return
}
var err error
var queryString string
switch queryRangeParams . CompositeQuery . QueryType {
case v3 . QueryTypeBuilder :
// check if any enrichment is required for logs if yes then enrich them
if logsv3 . EnrichmentRequired ( queryRangeParams ) {
2024-09-17 15:33:17 +05:30
logsFields , err := aH . reader . GetLogFields ( r . Context ( ) )
2023-07-20 17:53:55 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , nil )
return
}
2024-09-17 15:33:17 +05:30
// get the fields if any logs query is present
fields := model . GetLogFieldsV3 ( r . Context ( ) , queryRangeParams , logsFields )
2023-07-20 17:53:55 +05:30
logsv3 . Enrich ( queryRangeParams , fields )
}
queryString , err = aH . queryBuilder . PrepareLiveTailQuery ( queryRangeParams )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
default :
err = fmt . Errorf ( "invalid query type" )
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
// create the client
2024-09-13 18:10:49 +05:30
client := & model . LogsLiveTailClient { Name : r . RemoteAddr , Logs : make ( chan * model . SignozLog , 1000 ) , Done : make ( chan * bool ) , Error : make ( chan error ) }
2023-07-20 17:53:55 +05:30
go aH . reader . LiveTailLogsV3 ( r . Context ( ) , queryString , uint64 ( queryRangeParams . Start ) , "" , client )
w . Header ( ) . Set ( "Connection" , "keep-alive" )
w . Header ( ) . Set ( "Content-Type" , "text/event-stream" )
w . Header ( ) . Set ( "Cache-Control" , "no-cache" )
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , "*" )
w . WriteHeader ( 200 )
flusher , ok := w . ( http . Flusher )
if ! ok {
err := model . ApiError { Typ : model . ErrorStreamingNotSupported , Err : nil }
RespondError ( w , & err , "streaming is not supported" )
return
}
// flush the headers
flusher . Flush ( )
for {
select {
case log := <- client . Logs :
var buf bytes . Buffer
enc := json . NewEncoder ( & buf )
enc . Encode ( log )
2023-08-14 17:08:57 +05:30
fmt . Fprintf ( w , "data: %v\n\n" , buf . String ( ) )
2023-07-20 17:53:55 +05:30
flusher . Flush ( )
case <- client . Done :
2024-03-27 00:07:29 +05:30
zap . L ( ) . Debug ( "done!" )
2023-07-20 17:53:55 +05:30
return
case err := <- client . Error :
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error occurred" , zap . Error ( err ) )
2023-07-20 17:53:55 +05:30
fmt . Fprintf ( w , "event: error\ndata: %v\n\n" , err . Error ( ) )
flusher . Flush ( )
return
}
}
2024-09-13 17:04:22 +05:30
2023-07-20 17:53:55 +05:30
}
2024-01-16 16:56:20 +05:30
2024-02-11 00:31:47 +05:30
func ( aH * APIHandler ) getMetricMetadata ( w http . ResponseWriter , r * http . Request ) {
metricName := r . URL . Query ( ) . Get ( "metricName" )
serviceName := r . URL . Query ( ) . Get ( "serviceName" )
metricMetadata , err := aH . reader . GetMetricMetadata ( r . Context ( ) , metricName , serviceName )
if err != nil {
RespondError ( w , & model . ApiError { Err : err , Typ : model . ErrorInternal } , nil )
return
}
aH . WriteJSON ( w , r , metricMetadata )
}
2024-01-16 16:56:20 +05:30
func ( aH * APIHandler ) queryRangeV4 ( ctx context . Context , queryRangeParams * v3 . QueryRangeParamsV3 , w http . ResponseWriter , r * http . Request ) {
var result [ ] * v3 . Result
var err error
2024-05-15 18:52:01 +05:30
var errQuriesByName map [ string ] error
2024-01-16 16:56:20 +05:30
var spanKeys map [ string ] v3 . AttributeKey
if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeBuilder {
// check if any enrichment is required for logs if yes then enrich them
if logsv3 . EnrichmentRequired ( queryRangeParams ) {
// get the fields if any logs query is present
2024-09-17 15:33:17 +05:30
logsFields , err := aH . reader . GetLogFields ( r . Context ( ) )
2024-01-16 16:56:20 +05:30
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
2024-09-17 15:33:17 +05:30
RespondError ( w , apiErrObj , nil )
2024-01-16 16:56:20 +05:30
return
}
2024-09-17 15:33:17 +05:30
fields := model . GetLogFieldsV3 ( r . Context ( ) , queryRangeParams , logsFields )
2024-01-16 16:56:20 +05:30
logsv3 . Enrich ( queryRangeParams , fields )
}
spanKeys , err = aH . getSpanKeysV3 ( ctx , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
2024-11-22 12:00:29 +05:30
if aH . UseTraceNewSchema {
tracesV4 . Enrich ( queryRangeParams , spanKeys )
} else {
tracesV3 . Enrich ( queryRangeParams , spanKeys )
}
2024-01-16 16:56:20 +05:30
}
2024-07-31 16:00:57 +05:30
// WARN: Only works for AND operator in traces query
if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeBuilder {
// check if traceID is used as filter (with equal/similar operator) in traces query if yes add timestamp filter to queryRange params
isUsed , traceIDs := tracesV3 . TraceIdFilterUsedWithEqual ( queryRangeParams )
2024-09-13 16:43:56 +05:30
if isUsed && len ( traceIDs ) > 0 {
2024-07-31 16:00:57 +05:30
zap . L ( ) . Debug ( "traceID used as filter in traces query" )
// query signoz_spans table with traceID to get min and max timestamp
min , max , err := aH . reader . GetMinAndMaxTimestampForTraceID ( ctx , traceIDs )
if err == nil {
// add timestamp filter to queryRange params
tracesV3 . AddTimestampFilters ( min , max , queryRangeParams )
zap . L ( ) . Debug ( "post adding timestamp filter in traces query" , zap . Any ( "queryRangeParams" , queryRangeParams ) )
}
}
}
2024-09-13 16:43:56 +05:30
result , errQuriesByName , err = aH . querierV2 . QueryRange ( ctx , queryRangeParams )
2024-01-16 16:56:20 +05:30
if err != nil {
2024-09-13 18:01:37 +05:30
queryErrors := map [ string ] string { }
for name , err := range errQuriesByName {
queryErrors [ fmt . Sprintf ( "Query-%s" , name ) ] = err . Error ( )
}
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , queryErrors )
2024-01-16 16:56:20 +05:30
return
}
2024-01-25 01:14:45 +05:30
if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeBuilder {
2024-05-24 12:11:34 +05:30
result , err = postprocess . PostProcessResult ( result , queryRangeParams )
2024-06-25 13:42:40 +05:30
} else if queryRangeParams . CompositeQuery . QueryType == v3 . QueryTypeClickHouseSQL &&
queryRangeParams . CompositeQuery . PanelType == v3 . PanelTypeTable && queryRangeParams . FormatForWeb {
result = postprocess . TransformToTableForClickHouseQueries ( result )
2024-02-06 22:29:12 +05:30
}
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
2024-01-25 01:14:45 +05:30
}
2024-03-11 16:45:06 +05:30
sendQueryResultEvents ( r , result , queryRangeParams )
2024-01-16 16:56:20 +05:30
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
func ( aH * APIHandler ) QueryRangeV4 ( w http . ResponseWriter , r * http . Request ) {
queryRangeParams , apiErrorObj := ParseQueryRangeParams ( r )
if apiErrorObj != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "error parsing metric query range params" , zap . Error ( apiErrorObj . Err ) )
2024-01-16 16:56:20 +05:30
RespondError ( w , apiErrorObj , nil )
return
}
2024-06-25 13:42:40 +05:30
queryRangeParams . Version = "v4"
2024-01-16 16:56:20 +05:30
// add temporality for each metric
2024-09-24 10:22:52 +05:30
temporalityErr := aH . PopulateTemporality ( r . Context ( ) , queryRangeParams )
2024-01-16 16:56:20 +05:30
if temporalityErr != nil {
2024-03-27 00:07:29 +05:30
zap . L ( ) . Error ( "Error while adding temporality for metrics" , zap . Error ( temporalityErr ) )
2024-01-16 16:56:20 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : temporalityErr } , nil )
return
}
aH . queryRangeV4 ( r . Context ( ) , queryRangeParams , w , r )
}
2024-12-19 11:52:20 +07:00
func ( aH * APIHandler ) traceFields ( w http . ResponseWriter , r * http . Request ) {
fields , apiErr := aH . reader . GetTraceFields ( r . Context ( ) )
if apiErr != nil {
RespondError ( w , apiErr , "failed to fetch fields from the db" )
return
}
aH . WriteJSON ( w , r , fields )
}
func ( aH * APIHandler ) updateTraceField ( w http . ResponseWriter , r * http . Request ) {
field := model . UpdateField { }
if err := json . NewDecoder ( r . Body ) . Decode ( & field ) ; err != nil {
apiErr := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErr , "failed to decode payload" )
return
}
err := logs . ValidateUpdateFieldPayloadV2 ( & field )
if err != nil {
apiErr := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErr , "incorrect payload" )
return
}
apiErr := aH . reader . UpdateTraceField ( r . Context ( ) , & field )
if apiErr != nil {
RespondError ( w , apiErr , "failed to update field in the db" )
return
}
aH . WriteJSON ( w , r , field )
}