Compare commits
1 Commits
main
...
dashboard-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7edab87e52 |
@@ -301,6 +301,8 @@ func (aH *APIHandler) RegisterQueryRangeV3Routes(router *mux.Router, am *AuthMid
|
||||
subRouter.HandleFunc("/query_range", am.ViewAccess(aH.QueryRangeV3)).Methods(http.MethodPost)
|
||||
subRouter.HandleFunc("/query_range/format", am.ViewAccess(aH.QueryRangeV3Format)).Methods(http.MethodPost)
|
||||
|
||||
router.HandleFunc("/dashboards/variables/query", am.ViewAccess(aH.queryDashboardVarsV3)).Methods(http.MethodPost)
|
||||
|
||||
// live logs
|
||||
subRouter.HandleFunc("/logs/livetail", am.ViewAccess(aH.liveTailLogs)).Methods(http.MethodGet)
|
||||
}
|
||||
@@ -688,7 +690,6 @@ func (aH *APIHandler) deleteDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (aH *APIHandler) queryDashboardVars(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
query := r.URL.Query().Get("query")
|
||||
if query == "" {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("query is required")}, nil)
|
||||
@@ -3020,6 +3021,30 @@ func (aH *APIHandler) QueryRangeV3Format(w http.ResponseWriter, r *http.Request)
|
||||
aH.Respond(w, queryRangeParams)
|
||||
}
|
||||
|
||||
func (aH *APIHandler) queryDashboardVarsV3(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := parseTemplateVariableValueRequest(r)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
switch req.QueryType {
|
||||
case v3.QueryTypeBuilder:
|
||||
query, err := prepareQueryForTemplateVariableValue(req)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
|
||||
return
|
||||
}
|
||||
vars, err := aH.reader.QueryDashboardVars(r.Context(), query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, vars)
|
||||
case v3.QueryTypePromQL:
|
||||
}
|
||||
}
|
||||
|
||||
func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.QueryRangeParamsV3, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var result []*v3.Result
|
||||
|
||||
@@ -837,6 +837,140 @@ func parseAggregateAttributeRequest(r *http.Request) (*v3.AggregateAttributeRequ
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func parseTemplateVariableValueRequest(r *http.Request) (*v3.TmplVariableValueRequest, error) {
|
||||
var req v3.TmplVariableValueRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := req.QueryType.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.QueryType == v3.QueryTypeBuilder {
|
||||
if req.Builder == nil {
|
||||
return nil, fmt.Errorf("builder is required")
|
||||
}
|
||||
if err := req.Builder.AggregateAttribute.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Builder.Filters != nil {
|
||||
if err := req.Builder.Filters.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else if req.QueryType == v3.QueryTypePromQL {
|
||||
if req.PromQL == nil {
|
||||
return nil, fmt.Errorf("promql is required")
|
||||
}
|
||||
if len(req.PromQL.Query) == 0 {
|
||||
return nil, fmt.Errorf("query is required")
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid query type")
|
||||
}
|
||||
|
||||
formatterVars := make(map[string]interface{})
|
||||
for k, v := range req.Variables {
|
||||
if req.QueryType == v3.QueryTypeBuilder {
|
||||
formatterVars[k] = utils.ClickHouseFormattedValue(v)
|
||||
} else if req.QueryType == v3.QueryTypePromQL {
|
||||
formatterVars[k] = metrics.PromFormattedValue(v)
|
||||
}
|
||||
}
|
||||
req.Variables = formatterVars
|
||||
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func prepareQueryForMetrics(req *v3.TmplVariableValueRequest) (string, error) {
|
||||
var metricName string
|
||||
|
||||
for _, item := range req.Builder.Filters.Items {
|
||||
if item.Key.Key == "__name__" {
|
||||
metricName = item.Value.(string)
|
||||
}
|
||||
}
|
||||
|
||||
var conditions []string
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("metric_name = %s", metricName))
|
||||
|
||||
if req.Builder.Filters != nil && len(req.Builder.Filters.Items) != 0 {
|
||||
for _, item := range req.Builder.Filters.Items {
|
||||
toFormat := item.Value
|
||||
op := v3.FilterOperator(strings.ToLower(strings.TrimSpace(string(item.Operator))))
|
||||
if op == v3.FilterOperatorContains || op == v3.FilterOperatorNotContains {
|
||||
toFormat = fmt.Sprintf("%%%s%%", toFormat)
|
||||
}
|
||||
fmtVal := utils.ClickHouseFormattedValue(toFormat)
|
||||
switch op {
|
||||
case v3.FilterOperatorEqual:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') = %s", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorNotEqual:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') != %s", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorIn:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') IN %s", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorNotIn:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') NOT IN %s", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorLike:
|
||||
conditions = append(conditions, fmt.Sprintf("like(JSONExtractString(labels, '%s'), %s)", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorNotLike:
|
||||
conditions = append(conditions, fmt.Sprintf("notLike(JSONExtractString(labels, '%s'), %s)", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorRegex:
|
||||
conditions = append(conditions, fmt.Sprintf("match(JSONExtractString(labels, '%s'), %s)", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorNotRegex:
|
||||
conditions = append(conditions, fmt.Sprintf("not match(JSONExtractString(labels, '%s'), %s)", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorGreaterThan:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') > %s", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorGreaterThanOrEq:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') >= %s", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorLessThan:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') < %s", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorLessThanOrEq:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') <= %s", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorContains:
|
||||
conditions = append(conditions, fmt.Sprintf("like(JSONExtractString(labels, '%s'), %s)", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorNotContains:
|
||||
conditions = append(conditions, fmt.Sprintf("notLike(JSONExtractString(labels, '%s'), %s)", item.Key.Key, fmtVal))
|
||||
case v3.FilterOperatorExists:
|
||||
conditions = append(conditions, fmt.Sprintf("has(JSONExtractKeys(labels), '%s')", item.Key.Key))
|
||||
case v3.FilterOperatorNotExists:
|
||||
conditions = append(conditions, fmt.Sprintf("not has(JSONExtractKeys(labels), '%s')", item.Key.Key))
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported filter operator")
|
||||
}
|
||||
}
|
||||
}
|
||||
whereClause := strings.Join(conditions, " AND ")
|
||||
|
||||
for k, v := range req.Variables {
|
||||
whereClause = strings.Replace(whereClause, fmt.Sprintf("{{.%s}}", k), fmt.Sprint(v), -1)
|
||||
whereClause = strings.Replace(whereClause, fmt.Sprintf("{{%s}}", k), fmt.Sprint(v), -1)
|
||||
whereClause = strings.Replace(whereClause, fmt.Sprintf("[[%s]]", k), fmt.Sprint(v), -1)
|
||||
whereClause = strings.Replace(whereClause, fmt.Sprintf("$%s", k), fmt.Sprint(v), -1)
|
||||
}
|
||||
|
||||
label := req.Builder.AggregateAttribute.Key
|
||||
|
||||
query := fmt.Sprintf("SELECT JSONExtractString(labels, '%s') as %s FROM %s.%s WHERE %s GROUP BY %s",
|
||||
label, label, baseconstants.SIGNOZ_METRIC_DBNAME, baseconstants.SIGNOZ_TIMESERIES_v4_1DAY_TABLENAME, whereClause, label)
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
func prepareQueryForTemplateVariableValue(req *v3.TmplVariableValueRequest) (string, error) {
|
||||
var query string
|
||||
var err error
|
||||
switch req.Builder.DataSource {
|
||||
case v3.DataSourceMetrics:
|
||||
query, err = prepareQueryForMetrics(req)
|
||||
case v3.DataSourceLogs:
|
||||
case v3.DataSourceTraces:
|
||||
}
|
||||
return query, err
|
||||
}
|
||||
|
||||
func parseFilterAttributeKeyRequest(r *http.Request) (*v3.FilterAttributeKeyRequest, error) {
|
||||
var req v3.FilterAttributeKeyRequest
|
||||
|
||||
|
||||
@@ -275,6 +275,29 @@ func (q AttributeKeyDataType) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
type TmplVariableBuilderRequest struct {
|
||||
DataSource DataSource `json:"dataSource"`
|
||||
AggregateAttribute AttributeKey `json:"aggregateAttribute"`
|
||||
Filters *FilterSet `json:"filters,omitempty"`
|
||||
}
|
||||
|
||||
type TmplVariablePromQLRequest struct {
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
type TmplVariableValueRequest struct {
|
||||
QueryType QueryType `json:"queryType"`
|
||||
Builder *TmplVariableBuilderRequest `json:"builder,omitempty"`
|
||||
PromQL *TmplVariablePromQLRequest `json:"promql,omitempty"`
|
||||
Variables map[string]interface{} `json:"variables,omitempty"`
|
||||
}
|
||||
|
||||
type TmplVariableValueResponse struct {
|
||||
StringAttributeValues []string `json:"stringAttributeValues"`
|
||||
NumberAttributeValues []interface{} `json:"numberAttributeValues"`
|
||||
BoolAttributeValues []bool `json:"boolAttributeValues"`
|
||||
}
|
||||
|
||||
// FilterAttributeValueRequest is a request to fetch possible attribute values
|
||||
// for a selected aggregate operator, aggregate attribute, filter attribute key
|
||||
// and search text.
|
||||
|
||||
Reference in New Issue
Block a user