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"
2021-11-22 16:15:58 +05:30
"io/ioutil"
2021-01-03 18:15:44 +05:30
"net/http"
2023-07-13 14:22:30 +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"
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"
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"
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"
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"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/app/parser"
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"
2022-10-06 20:13:30 +05:30
"go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/constants"
2023-03-04 00:05:16 +05:30
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
2022-11-23 18:49:03 +05:30
querytemplate "go.signoz.io/signoz/pkg/query-service/utils/queryTemplate"
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"
2023-03-23 19:45:15 +05:30
"go.uber.org/multierr"
2021-01-03 18:15:44 +05:30
"go.uber.org/zap"
)
2021-08-29 10:28:40 +05:30
type status string
const (
statusSuccess status = "success"
statusError status = "error"
)
2021-01-03 18:15:44 +05:30
// NewRouter creates and configures a Gorilla Router.
func NewRouter ( ) * mux . Router {
return mux . NewRouter ( ) . UseEncodedPath ( )
}
// APIHandler implements the query service public API by registering routes at httpPrefix
type APIHandler struct {
// queryService *querysvc.QueryService
// queryParser queryParser
2023-07-14 11:31:44 +05:30
basePath string
apiPrefix string
reader interfaces . Reader
skipConfig * model . SkipConfig
appDao dao . ModelDao
alertManager am . Manager
ruleManager * rules . Manager
featureFlags interfaces . FeatureLookup
ready func ( http . HandlerFunc ) http . HandlerFunc
queryBuilder * queryBuilder . QueryBuilder
preferDelta bool
preferSpanMetrics bool
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
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
PerferDelta bool
PreferSpanMetrics bool
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
}
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
2022-07-14 11:59:06 +05:30
alertManager , err := am . New ( "" )
if err != nil {
return nil , err
}
2022-10-06 20:13:30 +05:30
2021-01-03 18:15:44 +05:30
aH := & APIHandler {
2023-07-14 11:31:44 +05:30
reader : opts . Reader ,
appDao : opts . AppDao ,
skipConfig : opts . SkipConfig ,
preferDelta : opts . PerferDelta ,
preferSpanMetrics : opts . PreferSpanMetrics ,
alertManager : alertManager ,
ruleManager : opts . RuleManager ,
featureFlags : opts . FeatureFlags ,
2021-01-03 18:15:44 +05:30
}
2022-10-06 20:13:30 +05:30
2023-05-09 19:16:55 +05:30
builderOpts := queryBuilder . QueryBuilderOptions {
2023-03-23 19:45:15 +05:30
BuildMetricQuery : metricsv3 . PrepareMetricQuery ,
2023-04-25 21:53:46 +05:30
BuildTraceQuery : tracesV3 . PrepareTracesQuery ,
BuildLogQuery : logsv3 . PrepareLogsQuery ,
2023-03-23 19:45:15 +05:30
}
2023-05-09 19:16:55 +05:30
aH . queryBuilder = queryBuilder . NewQueryBuilder ( builderOpts )
2023-03-23 19:45:15 +05:30
2021-08-29 10:28:40 +05:30
aH . ready = aH . testReady
2021-09-02 13:18:47 +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
zap . S ( ) . Warnf ( "unexpected error while fetch user count while initializing base api handler" , err . Error ( ) )
}
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
}
2021-09-02 13:18:47 +05:30
return aH , nil
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" `
}
type structuredError struct {
Code int ` json:"code,omitempty" `
Msg string ` json:"msg" `
// TraceID ui.TraceID `json:"traceID,omitempty"`
}
2021-08-29 10:28:40 +05:30
var corsHeaders = map [ string ] string {
"Access-Control-Allow-Headers" : "Accept, Authorization, Content-Type, Origin" ,
"Access-Control-Allow-Methods" : "GET, OPTIONS" ,
"Access-Control-Allow-Origin" : "*" ,
"Access-Control-Expose-Headers" : "Date" ,
}
// Enables cross-site script calls.
func setCORS ( w http . ResponseWriter ) {
for h , v := range corsHeaders {
w . Header ( ) . Set ( h , v )
}
}
type apiFunc func ( r * http . Request ) ( interface { } , * model . ApiError , func ( ) )
// Checks if server is ready, calls f if it is, returns 503 if it is not.
func ( aH * APIHandler ) testReady ( f http . HandlerFunc ) http . HandlerFunc {
return func ( w http . ResponseWriter , r * http . Request ) {
f ( w , r )
}
}
type response struct {
Status status ` json:"status" `
Data interface { } ` json:"data,omitempty" `
ErrorType model . ErrorType ` json:"errorType,omitempty" `
Error string ` json:"error,omitempty" `
}
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
b , err := json . Marshal ( & response {
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 {
zap . S ( ) . Error ( "msg" , "error marshalling json response" , "err" , err )
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
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 {
zap . S ( ) . Error ( "msg" , "error writing response" , "bytesWritten" , n , "err" , err )
}
}
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
b , err := json . Marshal ( & response {
Status : statusSuccess ,
Data : data ,
} )
if err != nil {
zap . S ( ) . Error ( "msg" , "error marshalling json response" , "err" , err )
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 {
zap . S ( ) . Error ( "msg" , "error writing response" , "bytesWritten" , n , "err" , err )
}
}
2022-10-06 20:13:30 +05:30
2023-02-15 23:49:03 +05:30
func ( aH * APIHandler ) RegisterMetricsRoutes ( router * mux . Router , am * AuthMiddleware ) {
2022-05-03 11:20:57 +05:30
subRouter := router . PathPrefix ( "/api/v2/metrics" ) . Subrouter ( )
2023-02-15 23:49:03 +05:30
subRouter . HandleFunc ( "/query_range" , am . ViewAccess ( aH . QueryRangeMetricsV2 ) ) . Methods ( http . MethodPost )
subRouter . HandleFunc ( "/autocomplete/list" , am . ViewAccess ( aH . metricAutocompleteMetricName ) ) . Methods ( http . MethodGet )
subRouter . HandleFunc ( "/autocomplete/tagKey" , am . ViewAccess ( aH . metricAutocompleteTagKey ) ) . Methods ( http . MethodGet )
subRouter . HandleFunc ( "/autocomplete/tagValue" , am . ViewAccess ( aH . metricAutocompleteTagValue ) ) . Methods ( http . MethodGet )
2022-05-03 11:20:57 +05:30
}
2021-08-29 10:28:40 +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-03-04 00:05:16 +05:30
}
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 )
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 )
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/grafana" , am . EditAccess ( aH . createDashboardsTransform ) ) . 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/v1/variables/query" , am . ViewAccess ( aH . queryDashboardVars ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v2/variables/query" , am . ViewAccess ( aH . queryDashboardVarsV2 ) ) . Methods ( http . MethodPost )
2023-03-07 00:26:25 +05:30
router . HandleFunc ( "/api/v1/explorer/queries" , am . ViewAccess ( aH . getExplorerQueries ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/explorer/queries" , am . EditAccess ( aH . createExplorerQueries ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/explorer/queries/{queryId}" , am . ViewAccess ( aH . getExplorerQuery ) ) . Methods ( http . MethodGet )
router . HandleFunc ( "/api/v1/explorer/queries/{queryId}" , am . EditAccess ( aH . updateExplorerQuery ) ) . Methods ( http . MethodPut )
router . HandleFunc ( "/api/v1/explorer/queries/{queryId}" , am . EditAccess ( aH . deleteExplorerQuery ) ) . Methods ( http . MethodDelete )
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/feedback" , am . OpenAccess ( aH . submitFeedback ) ) . Methods ( http . MethodPost )
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/overview" , am . ViewAccess ( aH . getServiceOverview ) ) . Methods ( http . MethodPost )
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 )
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
router . HandleFunc ( "/api/v1/getSpanFilters" , am . ViewAccess ( aH . getSpanFilters ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/getTagFilters" , am . ViewAccess ( aH . getTagFilters ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/getFilteredSpans" , am . ViewAccess ( aH . getFilteredSpans ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/getFilteredSpans/aggregates" , am . ViewAccess ( aH . getFilteredSpanAggregates ) ) . Methods ( http . MethodPost )
router . HandleFunc ( "/api/v1/getTagValues" , am . ViewAccess ( aH . getTagValues ) ) . Methods ( http . MethodPost )
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
// === Authentication APIs ===
2023-02-15 23:49:03 +05:30
router . HandleFunc ( "/api/v1/invite" , am . AdminAccess ( aH . inviteUser ) ) . Methods ( http . MethodPost )
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 )
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" ]
2022-07-14 11:59:06 +05:30
ruleResponse , err := aH . ruleManager . GetRule ( id )
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
}
2022-05-03 11:20:57 +05:30
func ( aH * APIHandler ) metricAutocompleteMetricName ( w http . ResponseWriter , r * http . Request ) {
matchText := r . URL . Query ( ) . Get ( "match" )
2022-06-24 14:52:11 +05:30
limit , err := strconv . Atoi ( r . URL . Query ( ) . Get ( "limit" ) )
if err != nil {
limit = 0 // no limit
}
2022-10-06 20:13:30 +05:30
metricNameList , apiErrObj := aH . reader . GetMetricAutocompleteMetricNames ( r . Context ( ) , matchText , limit )
2022-05-03 11:20:57 +05:30
if apiErrObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrObj , nil )
2022-05-03 11:20:57 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , metricNameList )
2022-05-03 11:20:57 +05:30
}
func ( aH * APIHandler ) metricAutocompleteTagKey ( w http . ResponseWriter , r * http . Request ) {
metricsAutocompleteTagKeyParams , apiErrorObj := parser . ParseMetricAutocompleteTagParams ( r )
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2022-05-03 11:20:57 +05:30
return
}
2022-10-06 20:13:30 +05:30
tagKeyList , apiErrObj := aH . reader . GetMetricAutocompleteTagKey ( r . Context ( ) , metricsAutocompleteTagKeyParams )
2022-05-03 11:20:57 +05:30
if apiErrObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrObj , nil )
2022-05-03 11:20:57 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , tagKeyList )
2022-05-03 11:20:57 +05:30
}
func ( aH * APIHandler ) metricAutocompleteTagValue ( w http . ResponseWriter , r * http . Request ) {
metricsAutocompleteTagValueParams , apiErrorObj := parser . ParseMetricAutocompleteTagParams ( r )
if len ( metricsAutocompleteTagValueParams . TagKey ) == 0 {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "tagKey not present in params" ) }
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrObj , nil )
2022-05-03 11:20:57 +05:30
return
}
if apiErrorObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2022-05-03 11:20:57 +05:30
return
}
2022-10-06 20:13:30 +05:30
tagValueList , apiErrObj := aH . reader . GetMetricAutocompleteTagValue ( r . Context ( ) , metricsAutocompleteTagValueParams )
2022-05-03 11:20:57 +05:30
if apiErrObj != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrObj , nil )
2022-05-03 11:20:57 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , tagValueList )
2022-05-03 11:20:57 +05:30
}
2023-07-13 18:50:19 +05:30
func ( aH * APIHandler ) addTemporality ( ctx context . Context , qp * v3 . QueryRangeParamsV3 ) error {
metricNames := 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 query . DataSource == v3 . DataSourceMetrics {
metricNames = append ( metricNames , query . AggregateAttribute . Key )
if _ , ok := metricNameToTemporality [ query . AggregateAttribute . Key ] ; ! ok {
metricNameToTemporality [ query . AggregateAttribute . Key ] = make ( map [ v3 . Temporality ] bool )
}
}
}
}
var err error
if aH . preferDelta {
zap . S ( ) . Debug ( "fetching metric temporality" )
metricNameToTemporality , err = aH . reader . FetchTemporality ( ctx , metricNames )
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 {
if aH . preferDelta && metricNameToTemporality [ query . AggregateAttribute . Key ] [ v3 . Delta ] {
query . Temporality = v3 . Delta
} else if metricNameToTemporality [ query . AggregateAttribute . Key ] [ v3 . Cumulative ] {
query . Temporality = v3 . Cumulative
} else {
query . Temporality = v3 . Unspecified
}
}
}
}
return nil
}
2022-11-24 18:18:19 +05:30
func ( aH * APIHandler ) QueryRangeMetricsV2 ( w http . ResponseWriter , r * http . Request ) {
2022-05-03 11:20:57 +05:30
metricsQueryRangeParams , apiErrorObj := parser . ParseMetricQueryRangeParams ( r )
if apiErrorObj != nil {
zap . S ( ) . Errorf ( apiErrorObj . Err . Error ( ) )
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrorObj , nil )
2022-05-03 11:20:57 +05:30
return
}
2022-06-24 14:52:11 +05:30
// prometheus instant query needs same timestamp
if metricsQueryRangeParams . CompositeMetricQuery . PanelType == model . QUERY_VALUE &&
metricsQueryRangeParams . CompositeMetricQuery . QueryType == model . PROM {
metricsQueryRangeParams . Start = metricsQueryRangeParams . End
}
// round up the end to neaerest multiple
if metricsQueryRangeParams . CompositeMetricQuery . QueryType == model . QUERY_BUILDER {
end := ( metricsQueryRangeParams . End ) / 1000
step := metricsQueryRangeParams . Step
metricsQueryRangeParams . End = ( end / step * step ) * 1000
}
type channelResult struct {
Series [ ] * model . Series
Err error
2022-09-11 03:34:02 +05:30
Name string
Query string
2022-06-24 14:52:11 +05:30
}
2022-09-11 03:34:02 +05:30
execClickHouseQueries := func ( queries map [ string ] string ) ( [ ] * model . Series , error , map [ string ] string ) {
2022-06-24 14:52:11 +05:30
var seriesList [ ] * model . Series
ch := make ( chan channelResult , len ( queries ) )
var wg sync . WaitGroup
for name , query := range queries {
wg . Add ( 1 )
go func ( name , query string ) {
defer wg . Done ( )
2022-10-06 20:13:30 +05:30
seriesList , err := aH . reader . GetMetricResult ( r . Context ( ) , query )
2022-06-24 14:52:11 +05:30
for _ , series := range seriesList {
series . QueryName = name
}
if err != nil {
2022-09-11 03:34:02 +05:30
ch <- channelResult { Err : fmt . Errorf ( "error in query-%s: %v" , name , err ) , Name : name , Query : query }
2022-06-24 14:52:11 +05:30
return
}
ch <- channelResult { Series : seriesList }
} ( name , query )
}
wg . Wait ( )
close ( ch )
var errs [ ] error
2022-09-11 03:34:02 +05:30
errQuriesByName := make ( map [ string ] string )
2022-06-24 14:52:11 +05:30
// read values from the channel
for r := range ch {
if r . Err != nil {
errs = append ( errs , r . Err )
2022-09-11 03:34:02 +05:30
errQuriesByName [ r . Name ] = r . Query
2022-06-24 14:52:11 +05:30
continue
}
seriesList = append ( seriesList , r . Series ... )
}
if len ( errs ) != 0 {
2022-09-11 03:34:02 +05:30
return nil , fmt . Errorf ( "encountered multiple errors: %s" , metrics . FormatErrs ( errs , "\n" ) ) , errQuriesByName
2022-06-24 14:52:11 +05:30
}
2022-09-11 03:34:02 +05:30
return seriesList , nil , nil
2022-06-24 14:52:11 +05:30
}
2022-09-11 03:34:02 +05:30
execPromQueries := func ( metricsQueryRangeParams * model . QueryRangeParamsV2 ) ( [ ] * model . Series , error , map [ string ] string ) {
2022-06-24 14:52:11 +05:30
var seriesList [ ] * model . Series
ch := make ( chan channelResult , len ( metricsQueryRangeParams . CompositeMetricQuery . PromQueries ) )
var wg sync . WaitGroup
for name , query := range metricsQueryRangeParams . CompositeMetricQuery . PromQueries {
if query . Disabled {
continue
}
wg . Add ( 1 )
go func ( name string , query * model . PromQuery ) {
var seriesList [ ] * model . Series
defer wg . Done ( )
2022-09-11 03:34:02 +05:30
tmpl := template . New ( "promql-query" )
tmpl , tmplErr := tmpl . Parse ( query . Query )
if tmplErr != nil {
ch <- channelResult { Err : fmt . Errorf ( "error in parsing query-%s: %v" , name , tmplErr ) , Name : name , Query : query . Query }
return
}
var queryBuf bytes . Buffer
tmplErr = tmpl . Execute ( & queryBuf , metricsQueryRangeParams . Variables )
if tmplErr != nil {
ch <- channelResult { Err : fmt . Errorf ( "error in parsing query-%s: %v" , name , tmplErr ) , Name : name , Query : query . Query }
return
}
query . Query = queryBuf . String ( )
2022-06-24 14:52:11 +05:30
queryModel := model . QueryRangeParams {
Start : time . UnixMilli ( metricsQueryRangeParams . Start ) ,
End : time . UnixMilli ( metricsQueryRangeParams . End ) ,
Step : time . Duration ( metricsQueryRangeParams . Step * int64 ( time . Second ) ) ,
Query : query . Query ,
}
2022-10-06 20:13:30 +05:30
promResult , _ , err := aH . reader . GetQueryRangeResult ( r . Context ( ) , & queryModel )
2022-06-24 14:52:11 +05:30
if err != nil {
2022-09-11 03:34:02 +05:30
ch <- channelResult { Err : fmt . Errorf ( "error in query-%s: %v" , name , err ) , Name : name , Query : query . Query }
2022-06-24 14:52:11 +05:30
return
}
matrix , _ := promResult . Matrix ( )
for _ , v := range matrix {
var s model . Series
s . QueryName = name
s . Labels = v . Metric . Copy ( ) . Map ( )
for _ , p := range v . Points {
s . Points = append ( s . Points , model . MetricPoint { Timestamp : p . T , Value : p . V } )
}
seriesList = append ( seriesList , & s )
}
ch <- channelResult { Series : seriesList }
} ( name , query )
}
wg . Wait ( )
close ( ch )
var errs [ ] error
2022-09-11 03:34:02 +05:30
errQuriesByName := make ( map [ string ] string )
2022-06-24 14:52:11 +05:30
// read values from the channel
for r := range ch {
if r . Err != nil {
errs = append ( errs , r . Err )
2022-09-11 03:34:02 +05:30
errQuriesByName [ r . Name ] = r . Query
2022-06-24 14:52:11 +05:30
continue
}
seriesList = append ( seriesList , r . Series ... )
}
if len ( errs ) != 0 {
2022-09-11 03:34:02 +05:30
return nil , fmt . Errorf ( "encountered multiple errors: %s" , metrics . FormatErrs ( errs , "\n" ) ) , errQuriesByName
2022-06-24 14:52:11 +05:30
}
2022-09-11 03:34:02 +05:30
return seriesList , nil , nil
2022-06-24 14:52:11 +05:30
}
var seriesList [ ] * model . Series
var err error
2022-09-11 03:34:02 +05:30
var errQuriesByName map [ string ] string
2022-06-24 14:52:11 +05:30
switch metricsQueryRangeParams . CompositeMetricQuery . QueryType {
case model . QUERY_BUILDER :
runQueries := metrics . PrepareBuilderMetricQueries ( metricsQueryRangeParams , constants . SIGNOZ_TIMESERIES_TABLENAME )
if runQueries . Err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : runQueries . Err } , nil )
2022-06-24 14:52:11 +05:30
return
}
2022-09-11 03:34:02 +05:30
seriesList , err , errQuriesByName = execClickHouseQueries ( runQueries . Queries )
2022-06-24 14:52:11 +05:30
case model . CLICKHOUSE :
queries := make ( map [ string ] string )
for name , chQuery := range metricsQueryRangeParams . CompositeMetricQuery . ClickHouseQueries {
if chQuery . Disabled {
continue
}
2022-09-11 03:34:02 +05:30
tmpl := template . New ( "clickhouse-query" )
tmpl , err := tmpl . Parse ( chQuery . Query )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2022-09-11 03:34:02 +05:30
return
}
var query bytes . Buffer
2022-11-23 18:49:03 +05:30
// replace go template variables
querytemplate . AssignReservedVars ( metricsQueryRangeParams )
2022-09-11 03:34:02 +05:30
err = tmpl . Execute ( & query , metricsQueryRangeParams . Variables )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2022-09-11 03:34:02 +05:30
return
}
2022-11-23 18:49:03 +05:30
2022-09-11 03:34:02 +05:30
queries [ name ] = query . String ( )
2022-06-24 14:52:11 +05:30
}
2022-09-11 03:34:02 +05:30
seriesList , err , errQuriesByName = execClickHouseQueries ( queries )
2022-06-24 14:52:11 +05:30
case model . PROM :
2022-09-11 03:34:02 +05:30
seriesList , err , errQuriesByName = execPromQueries ( metricsQueryRangeParams )
2022-06-24 14:52:11 +05:30
default :
err = fmt . Errorf ( "invalid query type" )
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , errQuriesByName )
2022-06-24 14:52:11 +05:30
return
}
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErrObj , errQuriesByName )
2022-06-24 14:52:11 +05:30
return
}
if metricsQueryRangeParams . CompositeMetricQuery . PanelType == model . QUERY_VALUE &&
len ( seriesList ) > 1 &&
( metricsQueryRangeParams . CompositeMetricQuery . QueryType == model . QUERY_BUILDER ||
metricsQueryRangeParams . CompositeMetricQuery . QueryType == model . CLICKHOUSE ) {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "invalid: query resulted in more than one series for value type" ) } , nil )
2022-06-24 14:52:11 +05:30
return
}
type ResponseFormat struct {
ResultType string ` json:"resultType" `
Result [ ] * model . Series ` json:"result" `
2022-05-03 11:20:57 +05:30
}
2022-06-24 14:52:11 +05:30
resp := ResponseFormat { ResultType : "matrix" , Result : seriesList }
2022-10-06 20:13:30 +05:30
aH . Respond ( w , resp )
2022-05-03 11:20:57 +05:30
}
2022-07-14 11:59:06 +05:30
func ( aH * APIHandler ) listRules ( w http . ResponseWriter , r * http . Request ) {
rules , err := aH . ruleManager . ListRuleStates ( )
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 ) {
allDashboards , err := dashboards . GetDashboards ( )
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , err , nil )
2021-09-02 13:18:47 +05:30
return
}
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-05-17 16:10:43 +05:30
err := dashboards . DeleteDashboard ( 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
}
2022-09-11 03:34:02 +05:30
func ( aH * APIHandler ) queryDashboardVars ( w http . ResponseWriter , r * http . Request ) {
query := r . URL . Query ( ) . Get ( "query" )
if query == "" {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "query is required" ) } , nil )
2022-09-11 03:34:02 +05:30
return
}
if strings . Contains ( strings . ToLower ( query ) , "alter table" ) {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : fmt . Errorf ( "query shouldn't alter data" ) } , nil )
2022-09-11 03:34:02 +05:30
return
}
2022-10-06 20:13:30 +05:30
dashboardVars , err := aH . reader . QueryDashboardVars ( r . Context ( ) , query )
2022-09-11 03:34:02 +05:30
if err != nil {
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
2022-09-11 03:34:02 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , dashboardVars )
2022-09-11 03:34:02 +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 ) {
return "" , fmt . Errorf ( "Operation %s is not allowed" , op )
}
}
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-05-17 16:10:43 +05:30
dashboard , apiError := dashboards . UpdateDashboard ( 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" ]
dashboard , apiError := dashboards . GetDashboard ( uuid )
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
}
2022-11-10 16:49:54 +05:30
func ( aH * APIHandler ) saveAndReturn ( w http . ResponseWriter , signozDashboard model . DashboardData ) {
toSave := make ( map [ string ] interface { } )
toSave [ "title" ] = signozDashboard . Title
toSave [ "description" ] = signozDashboard . Description
toSave [ "tags" ] = signozDashboard . Tags
toSave [ "layout" ] = signozDashboard . Layout
toSave [ "widgets" ] = signozDashboard . Widgets
toSave [ "variables" ] = signozDashboard . Variables
2023-05-17 16:10:43 +05:30
dashboard , apiError := dashboards . CreateDashboard ( toSave , aH . featureFlags )
2022-11-10 16:49:54 +05:30
if apiError != nil {
RespondError ( w , apiError , nil )
return
}
aH . Respond ( w , dashboard )
return
}
func ( aH * APIHandler ) createDashboardsTransform ( w http . ResponseWriter , r * http . Request ) {
defer r . Body . Close ( )
b , err := ioutil . ReadAll ( r . Body )
var importData model . GrafanaJSON
err = json . Unmarshal ( b , & importData )
if err == nil {
signozDashboard := dashboards . TransformGrafanaJSONToSignoz ( importData )
aH . saveAndReturn ( w , signozDashboard )
return
}
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , "Error while creating dashboard from grafana json" )
}
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-05-17 16:10:43 +05:30
dash , apiErr := dashboards . CreateDashboard ( 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 ( )
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
zap . S ( ) . Errorf ( "Error in getting req body in test rule API\n" , 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" ]
2022-07-14 11:59:06 +05:30
err := aH . ruleManager . DeleteRule ( 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 ( )
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
zap . S ( ) . Errorf ( "msg: error in getting req body of patch rule API\n" , "\t 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
}
gettableRule , err := aH . ruleManager . PatchRule ( string ( body ) , id )
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 ( )
body , err := ioutil . ReadAll ( r . Body )
2021-11-22 16:15:58 +05:30
if err != nil {
2022-07-14 11:59:06 +05:30
zap . S ( ) . Errorf ( "msg: error in getting req body of edit rule API\n" , "\t 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-07-14 11:59:06 +05:30
err = aH . ruleManager . EditRule ( 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" ]
2022-10-06 20:13:30 +05:30
channel , apiErrorObj := aH . reader . 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" ]
2022-10-06 20:13:30 +05:30
apiErrorObj := aH . reader . 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 ) {
2022-10-06 20:13:30 +05:30
channels , apiErrorObj := aH . reader . 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 ( )
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
zap . S ( ) . Errorf ( "Error in getting req body of testChannel API\n" , 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
zap . S ( ) . Errorf ( "Error in parsing req body of testChannel API\n" , 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 ( )
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
zap . S ( ) . Errorf ( "Error in getting req body of editChannel API\n" , 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
zap . S ( ) . Errorf ( "Error in parsing req body of editChannel API\n" , 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-10-06 20:13:30 +05:30
_ , apiErrorObj := aH . reader . 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 ( )
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
zap . S ( ) . Errorf ( "Error in getting req body of createChannel API\n" , 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
zap . S ( ) . Errorf ( "Error in parsing req body of createChannel API\n" , 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-10-06 20:13:30 +05:30
_ , apiErrorObj := aH . reader . 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
}
func ( aH * APIHandler ) createRule ( w http . ResponseWriter , r * http . Request ) {
2022-07-14 11:59:06 +05:30
defer r . Body . Close ( )
body , err := ioutil . ReadAll ( r . Body )
2021-11-22 16:15:58 +05:30
if err != nil {
2022-07-14 11:59:06 +05:30
zap . S ( ) . Errorf ( "Error in getting req body for create rule API\n" , 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-07-14 11:59:06 +05:30
err = aH . ruleManager . CreateRule ( string ( body ) )
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
}
2022-10-06 20:13:30 +05:30
aH . Respond ( w , "rule successfully added" )
2021-11-22 16:15:58 +05:30
}
2022-08-04 17:24:15 +05:30
2022-05-03 11:20:57 +05:30
func ( aH * APIHandler ) queryRangeMetricsFromClickhouse ( w http . ResponseWriter , r * http . Request ) {
2021-11-22 16:15:58 +05:30
2022-05-03 11:20:57 +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
}
// zap.S().Info(query, apiError)
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 {
zap . S ( ) . Error ( res . Err )
}
if res . Err != nil {
switch res . Err . ( type ) {
case promql . ErrQueryCanceled :
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { model . ErrorCanceled , res . Err } , nil )
2021-08-29 10:28:40 +05:30
case promql . ErrQueryTimeout :
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { model . ErrorTimeout , res . Err } , nil )
2021-08-29 10:28:40 +05:30
}
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { model . ErrorExec , 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
}
// zap.S().Info(query, apiError)
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 {
zap . S ( ) . Error ( res . Err )
}
if res . Err != nil {
switch res . Err . ( type ) {
case promql . ErrQueryCanceled :
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { model . ErrorCanceled , res . Err } , nil )
2021-08-29 10:28:40 +05:30
case promql . ErrQueryTimeout :
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { model . ErrorTimeout , res . Err } , nil )
2021-08-29 10:28:40 +05:30
}
2022-10-06 20:13:30 +05:30
RespondError ( w , & model . ApiError { model . ErrorExec , 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 ,
}
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_EVENT_INPRODUCT_FEEDBACK , data )
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
2021-05-29 16:32:11 +05:30
func ( aH * APIHandler ) getServiceOverview ( w http . ResponseWriter , r * http . Request ) {
2021-01-03 18:15:44 +05:30
2021-05-29 16:32:11 +05:30
query , err := parseGetServiceOverviewRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2021-05-29 16:32:11 +05:30
return
}
2021-01-03 18:15:44 +05:30
2023-06-30 06:58:22 +05:30
result , apiErr := aH . reader . GetServiceOverview ( r . Context ( ) , query , aH . skipConfig )
2022-10-06 20:13:30 +05:30
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2021-05-29 16:32: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-29 16:32:11 +05:30
}
2021-01-03 18:15:44 +05:30
2022-08-04 11:57:05 +05:30
func ( aH * APIHandler ) getServicesTopLevelOps ( w http . ResponseWriter , r * http . Request ) {
2023-06-30 06:58:22 +05:30
result , apiErr := aH . reader . GetTopLevelOperations ( r . Context ( ) , aH . skipConfig )
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 ) ,
}
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_EVENT_NUMBER_OF_SERVICES , data )
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
2022-11-24 18:18:19 +05:30
traceId , spanId , levelUpInt , levelDownInt , err := ParseSearchTracesParams ( r )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , "Error reading params" )
return
}
2021-01-03 18:15:44 +05:30
2022-11-24 18:18:19 +05:30
result , err := aH . reader . SearchTraces ( r . Context ( ) , traceId , spanId , levelUpInt , levelDownInt , 0 , 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
}
2022-01-26 20:41:59 +05:30
func ( aH * APIHandler ) getSpanFilters ( w http . ResponseWriter , r * http . Request ) {
2022-02-02 11:40:30 +05:30
query , err := parseSpanFilterRequestBody ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-26 20:41:59 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetSpanFilters ( r . Context ( ) , query )
2022-01-26 20:41:59 +05:30
2022-10-06 20:13:30 +05:30
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2022-01-26 20:41:59 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-01-26 20:41:59 +05:30
}
func ( aH * APIHandler ) getFilteredSpans ( w http . ResponseWriter , r * http . Request ) {
2022-11-09 08:30:00 +05:30
query , err := parseFilteredSpansRequest ( r , aH )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-26 20:41:59 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetFilteredSpans ( r . Context ( ) , query )
2022-01-26 20:41:59 +05:30
2022-10-06 20:13:30 +05:30
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2022-01-26 20:41:59 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-01-26 20:41:59 +05:30
}
func ( aH * APIHandler ) getFilteredSpanAggregates ( w http . ResponseWriter , r * http . Request ) {
query , err := parseFilteredSpanAggregatesRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-26 20:41:59 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetFilteredSpansAggregates ( r . Context ( ) , query )
2022-01-26 20:41:59 +05:30
2022-10-06 20:13:30 +05:30
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2022-01-26 20:41:59 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-01-26 20:41:59 +05:30
}
func ( aH * APIHandler ) getTagFilters ( w http . ResponseWriter , r * http . Request ) {
query , err := parseTagFilterRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-01-26 20:41:59 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetTagFilters ( r . Context ( ) , query )
2022-01-26 20:41:59 +05:30
2022-10-06 20:13:30 +05:30
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2022-02-08 23:05:50 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-02-08 23:05:50 +05:30
}
func ( aH * APIHandler ) getTagValues ( w http . ResponseWriter , r * http . Request ) {
query , err := parseTagValueRequest ( r )
2022-10-06 20:13:30 +05:30
if aH . HandleError ( w , err , http . StatusBadRequest ) {
2022-02-08 23:05:50 +05:30
return
}
2022-10-06 20:13:30 +05:30
result , apiErr := aH . reader . GetTagValues ( r . Context ( ) , query )
2022-02-08 23:05:50 +05:30
2022-10-06 20:13:30 +05:30
if apiErr != nil && aH . HandleError ( w , apiErr . Err , http . StatusInternalServerError ) {
2022-01-26 20:41:59 +05:30
return
}
2022-10-06 20:13:30 +05:30
aH . WriteJSON ( w , r , result )
2022-01-26 20:41:59 +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
}
2022-05-03 15:26:32 +05:30
ctx := auth . AttachJwtToContext ( context . Background ( ) , r )
resp , err := auth . Invite ( ctx , req )
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
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" ]
ctx := auth . AttachJwtToContext ( context . Background ( ) , r )
if err := auth . RevokeInvite ( ctx , 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
}
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 {
zap . S ( ) . Debugf ( "[listUsers] Failed to query list of users, err: %v" , 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 {
zap . S ( ) . Debugf ( "[getUser] Failed to query user, err: %v" , 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 ,
Err : errors . New ( "User not found" ) ,
} , 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 {
zap . S ( ) . Debugf ( "[editUser] Failed to query user, err: %v" , 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
}
if len ( update . ProfilePirctureURL ) > 0 {
old . ProfilePirctureURL = update . ProfilePirctureURL
}
_ , apiErr = dao . DB ( ) . EditUser ( ctx , & model . User {
Id : old . Id ,
Name : old . Name ,
OrgId : old . OrgId ,
Email : old . Email ,
Password : old . Password ,
CreatedAt : old . CreatedAt ,
ProfilePirctureURL : old . ProfilePirctureURL ,
} )
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 ,
Err : errors . New ( "User not found" ) ,
} , 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 ( )
b , err := ioutil . ReadAll ( r . Body )
if err != nil {
zap . S ( ) . Errorf ( "failed read user flags from http request for userId " , userId , "with error: " , err )
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 {
zap . S ( ) . Errorf ( "failed parsing user flags for userId " , userId , "with error: " , err )
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 ,
Err : errors . New ( "No user found" ) ,
} , 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 {
2022-05-03 15:26:32 +05:30
Err : errors . New ( "Cannot demote the last admin" ) ,
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
}
2022-05-04 14:27:32 +05:30
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_EVENT_ORG_SETTINGS , data )
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 {
zap . S ( ) . Debugf ( "resetPassword failed, err: %v\n" , 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
}
if err := auth . ChangePassword ( context . Background ( ) , req ) ; err != nil {
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 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 {
zap . S ( ) . Error ( "HTTP handler, Internal Server Error" , zap . Error ( err ) )
}
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
// 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 )
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 {
2022-10-06 20:13:30 +05:30
RespondError ( w , apiErr , "Failed to update filed 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
client := & model . LogsTailClient { Name : r . RemoteAddr , Logs : make ( chan * model . GetLogsResponse , 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 :
2022-07-19 16:34:33 +05:30
zap . S ( ) . Debug ( "done!" )
2022-07-18 16:37:46 +05:30
return
2022-07-27 15:58:58 +05:30
case err := <- client . Error :
zap . S ( ) . Error ( "error occured!" , 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-03-07 00:26:25 +05:30
func ( aH * APIHandler ) getExplorerQueries ( w http . ResponseWriter , r * http . Request ) {
queries , err := explorer . GetQueries ( )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , queries )
}
func ( aH * APIHandler ) createExplorerQueries ( w http . ResponseWriter , r * http . Request ) {
var query v3 . ExplorerQuery
err := json . NewDecoder ( r . Body ) . Decode ( & query )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
// validate the query
if err := query . Validate ( ) ; err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
uuid , err := explorer . CreateQuery ( query )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , uuid )
}
func ( aH * APIHandler ) getExplorerQuery ( w http . ResponseWriter , r * http . Request ) {
queryID := mux . Vars ( r ) [ "queryId" ]
query , err := explorer . GetQuery ( queryID )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , query )
}
func ( aH * APIHandler ) updateExplorerQuery ( w http . ResponseWriter , r * http . Request ) {
queryID := mux . Vars ( r ) [ "queryId" ]
var query v3 . ExplorerQuery
err := json . NewDecoder ( r . Body ) . Decode ( & query )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
// validate the query
if err := query . Validate ( ) ; err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
err = explorer . UpdateQuery ( queryID , query )
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorInternal , Err : err } , nil )
return
}
aH . Respond ( w , query )
}
func ( aH * APIHandler ) deleteExplorerQuery ( w http . ResponseWriter , r * http . Request ) {
queryID := mux . Vars ( r ) [ "queryId" ]
err := explorer . DeleteQuery ( queryID )
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 :
response , err = aH . reader . GetMetricAggregateAttributes ( r . Context ( ) , req )
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
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-10 19:36:13 +05:30
func ( aH * APIHandler ) execClickHouseGraphQueries ( ctx context . Context , queries map [ string ] string ) ( [ ] * v3 . Result , error , map [ string ] string ) {
2023-03-23 19:45:15 +05:30
type channelResult struct {
Series [ ] * v3 . Series
Err error
Name string
Query string
}
ch := make ( chan channelResult , len ( queries ) )
var wg sync . WaitGroup
for name , query := range queries {
wg . Add ( 1 )
go func ( name , query string ) {
defer wg . Done ( )
2023-07-16 23:07:45 +05:30
2023-03-23 19:45:15 +05:30
seriesList , err := aH . reader . GetTimeSeriesResultV3 ( ctx , query )
if err != nil {
ch <- channelResult { Err : fmt . Errorf ( "error in query-%s: %v" , name , err ) , Name : name , Query : query }
return
}
ch <- channelResult { Series : seriesList , Name : name , Query : query }
} ( name , query )
}
wg . Wait ( )
close ( ch )
var errs [ ] error
errQuriesByName := make ( map [ string ] string )
res := make ( [ ] * v3 . Result , 0 )
// read values from the channel
for r := range ch {
if r . Err != nil {
errs = append ( errs , r . Err )
errQuriesByName [ r . Name ] = r . Query
continue
}
res = append ( res , & v3 . Result {
QueryName : r . Name ,
Series : r . Series ,
} )
}
if len ( errs ) != 0 {
return nil , fmt . Errorf ( "encountered multiple errors: %s" , multierr . Combine ( errs ... ) ) , errQuriesByName
}
return res , nil , nil
}
2023-04-10 19:36:13 +05:30
func ( aH * APIHandler ) execClickHouseListQueries ( ctx context . Context , queries map [ string ] string ) ( [ ] * v3 . Result , error , map [ string ] string ) {
type channelResult struct {
List [ ] * v3 . Row
Err error
Name string
Query string
}
ch := make ( chan channelResult , len ( queries ) )
var wg sync . WaitGroup
for name , query := range queries {
wg . Add ( 1 )
go func ( name , query string ) {
defer wg . Done ( )
rowList , err := aH . reader . GetListResultV3 ( ctx , query )
if err != nil {
ch <- channelResult { Err : fmt . Errorf ( "error in query-%s: %v" , name , err ) , Name : name , Query : query }
return
}
ch <- channelResult { List : rowList , Name : name , Query : query }
} ( name , query )
}
wg . Wait ( )
close ( ch )
var errs [ ] error
errQuriesByName := make ( map [ string ] string )
res := make ( [ ] * v3 . Result , 0 )
// read values from the channel
for r := range ch {
if r . Err != nil {
errs = append ( errs , r . Err )
errQuriesByName [ r . Name ] = r . Query
continue
}
res = append ( res , & v3 . Result {
QueryName : r . Name ,
List : r . List ,
} )
}
if len ( errs ) != 0 {
return nil , fmt . Errorf ( "encountered multiple errors: %s" , multierr . Combine ( errs ... ) ) , errQuriesByName
}
return res , nil , nil
}
2023-03-23 19:45:15 +05:30
func ( aH * APIHandler ) execPromQueries ( ctx context . Context , metricsQueryRangeParams * v3 . QueryRangeParamsV3 ) ( [ ] * v3 . Result , error , map [ string ] string ) {
type channelResult struct {
Series [ ] * v3 . Series
Err error
Name string
Query string
}
ch := make ( chan channelResult , len ( metricsQueryRangeParams . CompositeQuery . PromQueries ) )
var wg sync . WaitGroup
for name , query := range metricsQueryRangeParams . CompositeQuery . PromQueries {
if query . Disabled {
continue
}
wg . Add ( 1 )
go func ( name string , query * v3 . PromQuery ) {
var seriesList [ ] * v3 . Series
defer wg . Done ( )
tmpl := template . New ( "promql-query" )
tmpl , tmplErr := tmpl . Parse ( query . Query )
if tmplErr != nil {
ch <- channelResult { Err : fmt . Errorf ( "error in parsing query-%s: %v" , name , tmplErr ) , Name : name , Query : query . Query }
return
}
var queryBuf bytes . Buffer
tmplErr = tmpl . Execute ( & queryBuf , metricsQueryRangeParams . Variables )
if tmplErr != nil {
ch <- channelResult { Err : fmt . Errorf ( "error in parsing query-%s: %v" , name , tmplErr ) , Name : name , Query : query . Query }
return
}
query . Query = queryBuf . String ( )
queryModel := model . QueryRangeParams {
Start : time . UnixMilli ( metricsQueryRangeParams . Start ) ,
End : time . UnixMilli ( metricsQueryRangeParams . End ) ,
Step : time . Duration ( metricsQueryRangeParams . Step * int64 ( time . Second ) ) ,
Query : query . Query ,
}
promResult , _ , err := aH . reader . GetQueryRangeResult ( ctx , & queryModel )
if err != nil {
ch <- channelResult { Err : fmt . Errorf ( "error in query-%s: %v" , name , err ) , Name : name , Query : query . Query }
return
}
matrix , _ := promResult . Matrix ( )
for _ , v := range matrix {
var s v3 . Series
s . Labels = v . Metric . Copy ( ) . Map ( )
for _ , p := range v . Points {
s . Points = append ( s . Points , v3 . Point { Timestamp : p . T , Value : p . V } )
}
seriesList = append ( seriesList , & s )
}
ch <- channelResult { Series : seriesList , Name : name , Query : query . Query }
} ( name , query )
}
wg . Wait ( )
close ( ch )
var errs [ ] error
errQuriesByName := make ( map [ string ] string )
res := make ( [ ] * v3 . Result , 0 )
// read values from the channel
for r := range ch {
if r . Err != nil {
errs = append ( errs , r . Err )
errQuriesByName [ r . Name ] = r . Query
continue
}
res = append ( res , & v3 . Result {
QueryName : r . Name ,
Series : r . Series ,
} )
}
if len ( errs ) != 0 {
return nil , fmt . Errorf ( "encountered multiple errors: %s" , multierr . Combine ( errs ... ) ) , errQuriesByName
}
return res , nil , nil
}
2023-04-10 19:36:13 +05:30
func ( aH * APIHandler ) getLogFieldsV3 ( 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 . DataSourceLogs {
fields , apiError := aH . reader . GetLogFields ( ctx )
if apiError != nil {
return nil , apiError . Err
}
// top level fields meta will always be present in the frontend. (can be support for that as enchancement)
getType := func ( t string ) ( v3 . AttributeKeyType , bool ) {
if t == "attributes" {
return v3 . AttributeKeyTypeTag , false
} else if t == "resources" {
return v3 . AttributeKeyTypeResource , false
}
return "" , true
}
for _ , selectedField := range fields . Selected {
fieldType , pass := getType ( selectedField . Type )
if pass {
continue
}
data [ selectedField . Name ] = v3 . AttributeKey {
Key : selectedField . Name ,
Type : fieldType ,
DataType : v3 . AttributeKeyDataType ( strings . ToLower ( selectedField . DataType ) ) ,
IsColumn : true ,
}
}
for _ , interestingField := range fields . Interesting {
fieldType , pass := getType ( interestingField . Type )
if pass {
continue
}
data [ interestingField . Name ] = v3 . AttributeKey {
Key : interestingField . Name ,
Type : fieldType ,
DataType : v3 . AttributeKeyDataType ( strings . ToLower ( interestingField . DataType ) ) ,
IsColumn : false ,
}
}
break
}
}
return data , nil
}
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-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
var errQuriesByName map [ string ] string
2023-04-10 19:36:13 +05:30
var queries map [ string ] string
2023-03-23 19:45:15 +05:30
switch queryRangeParams . CompositeQuery . QueryType {
case 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 ) {
// get the fields if any logs query is present
var fields map [ string ] v3 . AttributeKey
fields , err = aH . getLogFieldsV3 ( ctx , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
logsv3 . Enrich ( queryRangeParams , fields )
2023-04-10 19:36:13 +05:30
}
2023-04-25 21:53:46 +05:30
var spanKeys map [ string ] v3 . AttributeKey
spanKeys , err = aH . getSpanKeysV3 ( ctx , queryRangeParams )
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorInternal , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
2023-06-09 17:07:45 +05:30
queries , err = aH . queryBuilder . PrepareQueries ( queryRangeParams , spanKeys )
2023-03-23 19:45:15 +05:30
if err != nil {
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , nil )
return
}
2023-04-10 19:36:13 +05:30
2023-07-05 06:57:39 +05:30
if queryRangeParams . CompositeQuery . PanelType == v3 . PanelTypeList || queryRangeParams . CompositeQuery . PanelType == v3 . PanelTypeTrace {
2023-04-10 19:36:13 +05:30
result , err , errQuriesByName = aH . execClickHouseListQueries ( r . Context ( ) , queries )
} else {
result , err , errQuriesByName = aH . execClickHouseGraphQueries ( r . Context ( ) , queries )
}
2023-03-23 19:45:15 +05:30
case v3 . QueryTypeClickHouseSQL :
queries := make ( map [ string ] string )
for name , query := range queryRangeParams . CompositeQuery . ClickHouseQueries {
if query . Disabled {
continue
}
queries [ name ] = query . Query
}
2023-04-10 19:36:13 +05:30
result , err , errQuriesByName = aH . execClickHouseGraphQueries ( r . Context ( ) , queries )
2023-03-23 19:45:15 +05:30
case v3 . QueryTypePromQL :
result , err , errQuriesByName = aH . execPromQueries ( r . Context ( ) , queryRangeParams )
default :
err = fmt . Errorf ( "invalid query type" )
RespondError ( w , & model . ApiError { Typ : model . ErrorBadData , Err : err } , errQuriesByName )
return
}
if err != nil {
apiErrObj := & model . ApiError { Typ : model . ErrorBadData , Err : err }
RespondError ( w , apiErrObj , errQuriesByName )
return
}
2023-07-13 14:22:30 +05:30
applyMetricLimit ( result , queryRangeParams )
2023-03-23 19:45:15 +05:30
resp := v3 . QueryRangeResponse {
Result : result ,
}
aH . Respond ( w , resp )
}
func ( aH * APIHandler ) QueryRangeV3 ( w http . ResponseWriter , r * http . Request ) {
queryRangeParams , apiErrorObj := ParseQueryRangeParams ( r )
if apiErrorObj != nil {
zap . S ( ) . Errorf ( apiErrorObj . Err . Error ( ) )
RespondError ( w , apiErrorObj , nil )
return
}
2023-07-13 18:50:19 +05:30
// add temporality for each metric
temporalityErr := aH . addTemporality ( r . Context ( ) , queryRangeParams )
if temporalityErr != nil {
zap . S ( ) . Errorf ( "Error while adding temporality for metrics: %v" , temporalityErr )
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
func applyMetricLimit ( results [ ] * v3 . Result , queryRangeParams * v3 . QueryRangeParamsV3 ) {
// apply limit if any for metrics
// use the grouping set points to apply the limit
for _ , result := range results {
builderQueries := queryRangeParams . CompositeQuery . BuilderQueries
if builderQueries != nil && builderQueries [ result . QueryName ] . DataSource == v3 . DataSourceMetrics {
limit := builderQueries [ result . QueryName ] . Limit
var orderAsc bool
for _ , item := range builderQueries [ result . QueryName ] . OrderBy {
if item . ColumnName == constants . SigNozOrderByValue {
orderAsc = strings . ToLower ( item . Order ) == "asc"
break
}
}
if limit != 0 {
sort . Slice ( result . Series , func ( i , j int ) bool {
if orderAsc {
return result . Series [ i ] . Points [ 0 ] . Value < result . Series [ j ] . Points [ 0 ] . Value
}
return result . Series [ i ] . Points [ 0 ] . Value > result . Series [ j ] . Points [ 0 ] . Value
} )
if len ( result . Series ) > int ( limit ) {
result . Series = result . Series [ : limit ]
}
}
}
}
}