mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-06-04 11:25:52 +08:00
975 lines
24 KiB
Go
975 lines
24 KiB
Go
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
promModel "github.com/prometheus/common/model"
|
|
|
|
"go.signoz.io/query-service/constants"
|
|
"go.signoz.io/query-service/model"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
var allowedDimesions = []string{"calls", "duration"}
|
|
|
|
var allowedFunctions = []string{"count", "ratePerSec", "sum", "avg", "min", "max", "p50", "p90", "p95", "p99"}
|
|
|
|
var allowedAggregations = map[string][]string{
|
|
"calls": {"count", "rate_per_sec"},
|
|
"duration": {"avg", "p50", "p95", "p90", "p99", "min", "max", "sum"},
|
|
}
|
|
|
|
func parseUser(r *http.Request) (*model.User, error) {
|
|
|
|
var user model.User
|
|
err := json.NewDecoder(r.Body).Decode(&user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(user.Email) == 0 {
|
|
return nil, fmt.Errorf("email field not found")
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
func parseGetTopEndpointsRequest(r *http.Request) (*model.GetTopEndpointsParams, error) {
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTime("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
serviceName := r.URL.Query().Get("service")
|
|
if len(serviceName) == 0 {
|
|
return nil, errors.New("serviceName param missing in query")
|
|
}
|
|
|
|
getTopEndpointsParams := model.GetTopEndpointsParams{
|
|
StartTime: startTime.Format(time.RFC3339Nano),
|
|
EndTime: endTime.Format(time.RFC3339Nano),
|
|
ServiceName: serviceName,
|
|
Start: startTime,
|
|
End: endTime,
|
|
}
|
|
|
|
return &getTopEndpointsParams, nil
|
|
|
|
}
|
|
|
|
func parseMetricsTime(s string) (time.Time, error) {
|
|
if t, err := strconv.ParseFloat(s, 64); err == nil {
|
|
s, ns := math.Modf(t)
|
|
return time.Unix(int64(s), int64(ns*float64(time.Second))), nil
|
|
// return time.Unix(0, t), nil
|
|
}
|
|
if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
|
|
return t, nil
|
|
}
|
|
return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s)
|
|
}
|
|
|
|
func parseMetricsDuration(s string) (time.Duration, error) {
|
|
if d, err := strconv.ParseFloat(s, 64); err == nil {
|
|
ts := d * float64(time.Second)
|
|
if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
|
|
return 0, fmt.Errorf("cannot parse %q to a valid duration. It overflows int64", s)
|
|
}
|
|
return time.Duration(ts), nil
|
|
}
|
|
if d, err := promModel.ParseDuration(s); err == nil {
|
|
return time.Duration(d), nil
|
|
}
|
|
return 0, fmt.Errorf("cannot parse %q to a valid duration", s)
|
|
}
|
|
|
|
func parseInstantQueryMetricsRequest(r *http.Request) (*model.InstantQueryMetricsParams, *model.ApiError) {
|
|
var ts time.Time
|
|
if t := r.FormValue("time"); t != "" {
|
|
var err error
|
|
ts, err = parseMetricsTime(t)
|
|
if err != nil {
|
|
return nil, &model.ApiError{model.ErrorBadData, err}
|
|
}
|
|
} else {
|
|
ts = time.Now()
|
|
}
|
|
|
|
return &model.InstantQueryMetricsParams{
|
|
Time: ts,
|
|
Query: r.FormValue("query"),
|
|
Stats: r.FormValue("stats"),
|
|
}, nil
|
|
|
|
}
|
|
|
|
func parseQueryRangeRequest(r *http.Request) (*model.QueryRangeParams, *model.ApiError) {
|
|
|
|
start, err := parseMetricsTime(r.FormValue("start"))
|
|
if err != nil {
|
|
return nil, &model.ApiError{model.ErrorBadData, err}
|
|
}
|
|
end, err := parseMetricsTime(r.FormValue("end"))
|
|
if err != nil {
|
|
return nil, &model.ApiError{model.ErrorBadData, err}
|
|
}
|
|
if end.Before(start) {
|
|
err := errors.New("end timestamp must not be before start time")
|
|
return nil, &model.ApiError{model.ErrorBadData, err}
|
|
}
|
|
|
|
step, err := parseMetricsDuration(r.FormValue("step"))
|
|
if err != nil {
|
|
return nil, &model.ApiError{model.ErrorBadData, err}
|
|
}
|
|
|
|
if step <= 0 {
|
|
err := errors.New("zero or negative query resolution step widths are not accepted. Try a positive integer")
|
|
return nil, &model.ApiError{model.ErrorBadData, err}
|
|
}
|
|
|
|
// For safety, limit the number of returned points per timeseries.
|
|
// This is sufficient for 60s resolution for a week or 1h resolution for a year.
|
|
if end.Sub(start)/step > 11000 {
|
|
err := errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)")
|
|
return nil, &model.ApiError{model.ErrorBadData, err}
|
|
}
|
|
|
|
queryRangeParams := model.QueryRangeParams{
|
|
Start: start,
|
|
End: end,
|
|
Step: step,
|
|
Query: r.FormValue("query"),
|
|
Stats: r.FormValue("stats"),
|
|
}
|
|
|
|
return &queryRangeParams, nil
|
|
}
|
|
|
|
func parseGetUsageRequest(r *http.Request) (*model.GetUsageParams, error) {
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTime("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stepStr := r.URL.Query().Get("step")
|
|
if len(stepStr) == 0 {
|
|
return nil, errors.New("step param missing in query")
|
|
}
|
|
stepInt, err := strconv.Atoi(stepStr)
|
|
if err != nil {
|
|
return nil, errors.New("step param is not in correct format")
|
|
}
|
|
|
|
serviceName := r.URL.Query().Get("service")
|
|
stepHour := stepInt / 3600
|
|
|
|
getUsageParams := model.GetUsageParams{
|
|
StartTime: startTime.Format(time.RFC3339Nano),
|
|
EndTime: endTime.Format(time.RFC3339Nano),
|
|
Start: startTime,
|
|
End: endTime,
|
|
ServiceName: serviceName,
|
|
Period: fmt.Sprintf("PT%dH", stepHour),
|
|
StepHour: stepHour,
|
|
}
|
|
|
|
return &getUsageParams, nil
|
|
|
|
}
|
|
|
|
func parseGetServiceExternalRequest(r *http.Request) (*model.GetServiceOverviewParams, error) {
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTime("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stepStr := r.URL.Query().Get("step")
|
|
if len(stepStr) == 0 {
|
|
return nil, errors.New("step param missing in query")
|
|
}
|
|
stepInt, err := strconv.Atoi(stepStr)
|
|
if err != nil {
|
|
return nil, errors.New("step param is not in correct format")
|
|
}
|
|
|
|
serviceName := r.URL.Query().Get("service")
|
|
if len(serviceName) == 0 {
|
|
return nil, errors.New("serviceName param missing in query")
|
|
}
|
|
|
|
getServiceOverviewParams := model.GetServiceOverviewParams{
|
|
Start: startTime,
|
|
StartTime: startTime.Format(time.RFC3339Nano),
|
|
End: endTime,
|
|
EndTime: endTime.Format(time.RFC3339Nano),
|
|
ServiceName: serviceName,
|
|
Period: fmt.Sprintf("PT%dM", stepInt/60),
|
|
StepSeconds: stepInt,
|
|
}
|
|
|
|
return &getServiceOverviewParams, nil
|
|
|
|
}
|
|
|
|
func parseGetServiceOverviewRequest(r *http.Request) (*model.GetServiceOverviewParams, error) {
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTime("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stepStr := r.URL.Query().Get("step")
|
|
if len(stepStr) == 0 {
|
|
return nil, errors.New("step param missing in query")
|
|
}
|
|
stepInt, err := strconv.Atoi(stepStr)
|
|
if err != nil {
|
|
return nil, errors.New("step param is not in correct format")
|
|
}
|
|
|
|
serviceName := r.URL.Query().Get("service")
|
|
if len(serviceName) == 0 {
|
|
return nil, errors.New("serviceName param missing in query")
|
|
}
|
|
|
|
getServiceOverviewParams := model.GetServiceOverviewParams{
|
|
Start: startTime,
|
|
StartTime: startTime.Format(time.RFC3339Nano),
|
|
End: endTime,
|
|
EndTime: endTime.Format(time.RFC3339Nano),
|
|
ServiceName: serviceName,
|
|
Period: fmt.Sprintf("PT%dM", stepInt/60),
|
|
StepSeconds: stepInt,
|
|
}
|
|
|
|
return &getServiceOverviewParams, nil
|
|
|
|
}
|
|
|
|
func parseGetServicesRequest(r *http.Request) (*model.GetServicesParams, error) {
|
|
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTime("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
getServicesParams := model.GetServicesParams{
|
|
Start: startTime,
|
|
StartTime: startTime.Format(time.RFC3339Nano),
|
|
End: endTime,
|
|
EndTime: endTime.Format(time.RFC3339Nano),
|
|
Period: int(endTime.Unix() - startTime.Unix()),
|
|
}
|
|
return &getServicesParams, nil
|
|
|
|
}
|
|
|
|
func DoesExistInSlice(item string, list []string) bool {
|
|
for _, element := range list {
|
|
if item == element {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func parseSearchSpanAggregatesRequest(r *http.Request) (*model.SpanSearchAggregatesParams, error) {
|
|
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTime("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
startTimeStr := startTime.Format(time.RFC3339Nano)
|
|
endTimeStr := endTime.Format(time.RFC3339Nano)
|
|
// fmt.Println(startTimeStr)
|
|
|
|
stepStr := r.URL.Query().Get("step")
|
|
if len(stepStr) == 0 {
|
|
return nil, errors.New("step param missing in query")
|
|
}
|
|
|
|
stepInt, err := strconv.Atoi(stepStr)
|
|
if err != nil {
|
|
return nil, errors.New("step param is not in correct format")
|
|
}
|
|
|
|
granPeriod := fmt.Sprintf("PT%dM", stepInt/60)
|
|
dimension := r.URL.Query().Get("dimension")
|
|
if len(dimension) == 0 {
|
|
return nil, errors.New("dimension param missing in query")
|
|
} else {
|
|
if !DoesExistInSlice(dimension, allowedDimesions) {
|
|
return nil, errors.New(fmt.Sprintf("given dimension: %s is not allowed in query", dimension))
|
|
}
|
|
}
|
|
|
|
aggregationOption := r.URL.Query().Get("aggregation_option")
|
|
if len(aggregationOption) == 0 {
|
|
return nil, errors.New("Aggregation Option missing in query params")
|
|
} else {
|
|
if !DoesExistInSlice(aggregationOption, allowedAggregations[dimension]) {
|
|
return nil, errors.New(fmt.Sprintf("given aggregation option: %s is not allowed with dimension: %s", aggregationOption, dimension))
|
|
}
|
|
}
|
|
|
|
params := &model.SpanSearchAggregatesParams{
|
|
Start: startTime,
|
|
End: endTime,
|
|
Intervals: fmt.Sprintf("%s/%s", startTimeStr, endTimeStr),
|
|
GranOrigin: startTimeStr,
|
|
GranPeriod: granPeriod,
|
|
StepSeconds: stepInt,
|
|
Dimension: dimension,
|
|
AggregationOption: aggregationOption,
|
|
}
|
|
|
|
serviceName := r.URL.Query().Get("service")
|
|
if len(serviceName) != 0 {
|
|
// return nil, errors.New("serviceName param missing in query")
|
|
params.ServiceName = serviceName
|
|
}
|
|
operationName := r.URL.Query().Get("operation")
|
|
if len(operationName) != 0 {
|
|
params.OperationName = operationName
|
|
// Escaping new line chars to avoid CWE-117
|
|
operationName = strings.Replace(operationName, "\n", "", -1)
|
|
operationName = strings.Replace(operationName, "\r", "", -1)
|
|
zap.S().Debug("Operation Name: ", operationName)
|
|
}
|
|
|
|
kind := r.URL.Query().Get("kind")
|
|
if len(kind) != 0 {
|
|
params.Kind = kind
|
|
// Escaping new line chars to avoid CWE-117
|
|
kind = strings.Replace(kind, "\n", "", -1)
|
|
kind = strings.Replace(kind, "\r", "", -1)
|
|
zap.S().Debug("Kind: ", kind)
|
|
}
|
|
|
|
minDuration, err := parseTimestamp("minDuration", r)
|
|
if err == nil {
|
|
params.MinDuration = *minDuration
|
|
}
|
|
maxDuration, err := parseTimestamp("maxDuration", r)
|
|
if err == nil {
|
|
params.MaxDuration = *maxDuration
|
|
}
|
|
|
|
tags, err := parseTags("tags", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(*tags) != 0 {
|
|
params.Tags = *tags
|
|
}
|
|
|
|
return params, nil
|
|
}
|
|
|
|
func parseSpanSearchRequest(r *http.Request) (*model.SpanSearchParams, error) {
|
|
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTimeMinusBuffer("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
startTimeStr := startTime.Format(time.RFC3339Nano)
|
|
endTimeStr := endTime.Format(time.RFC3339Nano)
|
|
// fmt.Println(startTimeStr)
|
|
params := &model.SpanSearchParams{
|
|
Intervals: fmt.Sprintf("%s/%s", startTimeStr, endTimeStr),
|
|
Start: startTime,
|
|
End: endTime,
|
|
Limit: 100,
|
|
Order: "descending",
|
|
}
|
|
|
|
serviceName := r.URL.Query().Get("service")
|
|
if len(serviceName) != 0 {
|
|
// return nil, errors.New("serviceName param missing in query")
|
|
params.ServiceName = serviceName
|
|
}
|
|
operationName := r.URL.Query().Get("operation")
|
|
if len(operationName) != 0 {
|
|
params.OperationName = operationName
|
|
// Escaping new line chars to avoid CWE-117
|
|
operationName = strings.Replace(operationName, "\n", "", -1)
|
|
operationName = strings.Replace(operationName, "\r", "", -1)
|
|
zap.S().Debug("Operation Name: ", operationName)
|
|
}
|
|
|
|
kind := r.URL.Query().Get("kind")
|
|
if len(kind) != 0 {
|
|
params.Kind = kind
|
|
// Escaping new line chars to avoid CWE-117
|
|
kind = strings.Replace(kind, "\n", "", -1)
|
|
kind = strings.Replace(kind, "\r", "", -1)
|
|
zap.S().Debug("Kind: ", kind)
|
|
}
|
|
|
|
minDuration, err := parseTimestamp("minDuration", r)
|
|
if err == nil {
|
|
params.MinDuration = *minDuration
|
|
}
|
|
maxDuration, err := parseTimestamp("maxDuration", r)
|
|
if err == nil {
|
|
params.MaxDuration = *maxDuration
|
|
}
|
|
|
|
limitStr := r.URL.Query().Get("limit")
|
|
if len(limitStr) != 0 {
|
|
limit, err := strconv.ParseInt(limitStr, 10, 64)
|
|
if err != nil {
|
|
return nil, errors.New("Limit param is not in correct format")
|
|
}
|
|
params.Limit = limit
|
|
} else {
|
|
params.Limit = 100
|
|
}
|
|
|
|
offsetStr := r.URL.Query().Get("offset")
|
|
if len(offsetStr) != 0 {
|
|
offset, err := strconv.ParseInt(offsetStr, 10, 64)
|
|
if err != nil {
|
|
return nil, errors.New("Offset param is not in correct format")
|
|
}
|
|
params.Offset = offset
|
|
}
|
|
|
|
tags, err := parseTags("tags", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(*tags) != 0 {
|
|
params.Tags = *tags
|
|
}
|
|
|
|
return params, nil
|
|
}
|
|
|
|
func parseSpanFilterRequestBody(r *http.Request) (*model.SpanFilterParams, error) {
|
|
|
|
var postData *model.SpanFilterParams
|
|
err := json.NewDecoder(r.Body).Decode(&postData)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
postData.Start, err = parseTimeStr(postData.StartStr, "start")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return postData, nil
|
|
}
|
|
|
|
func parseFilteredSpansRequest(r *http.Request) (*model.GetFilteredSpansParams, error) {
|
|
|
|
var postData *model.GetFilteredSpansParams
|
|
err := json.NewDecoder(r.Body).Decode(&postData)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
postData.Start, err = parseTimeStr(postData.StartStr, "start")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if postData.Limit == 0 {
|
|
postData.Limit = 100
|
|
}
|
|
|
|
return postData, nil
|
|
}
|
|
|
|
func parseFilteredSpanAggregatesRequest(r *http.Request) (*model.GetFilteredSpanAggregatesParams, error) {
|
|
|
|
var postData *model.GetFilteredSpanAggregatesParams
|
|
err := json.NewDecoder(r.Body).Decode(&postData)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
postData.Start, err = parseTimeStr(postData.StartStr, "start")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
step := postData.StepSeconds
|
|
if step == 0 {
|
|
return nil, errors.New("step param missing in query")
|
|
}
|
|
|
|
function := postData.Function
|
|
if len(function) == 0 {
|
|
return nil, errors.New("function param missing in query")
|
|
} else {
|
|
if !DoesExistInSlice(function, allowedFunctions) {
|
|
return nil, errors.New(fmt.Sprintf("given function: %s is not allowed in query", function))
|
|
}
|
|
}
|
|
|
|
var dimension, aggregationOption string
|
|
|
|
switch function {
|
|
case "count":
|
|
dimension = "calls"
|
|
aggregationOption = "count"
|
|
case "ratePerSec":
|
|
dimension = "calls"
|
|
aggregationOption = "rate_per_sec"
|
|
case "avg":
|
|
dimension = "duration"
|
|
aggregationOption = "avg"
|
|
case "sum":
|
|
dimension = "duration"
|
|
aggregationOption = "sum"
|
|
case "p50":
|
|
dimension = "duration"
|
|
aggregationOption = "p50"
|
|
case "p90":
|
|
dimension = "duration"
|
|
aggregationOption = "p90"
|
|
case "p95":
|
|
dimension = "duration"
|
|
aggregationOption = "p95"
|
|
case "p99":
|
|
dimension = "duration"
|
|
aggregationOption = "p99"
|
|
case "min":
|
|
dimension = "duration"
|
|
aggregationOption = "min"
|
|
case "max":
|
|
dimension = "duration"
|
|
aggregationOption = "max"
|
|
}
|
|
|
|
postData.AggregationOption = aggregationOption
|
|
postData.Dimension = dimension
|
|
// tags, err := parseTagsV2("tags", r)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// if len(*tags) != 0 {
|
|
// params.Tags = *tags
|
|
// }
|
|
|
|
return postData, nil
|
|
}
|
|
|
|
func parseErrorRequest(r *http.Request) (*model.GetErrorParams, error) {
|
|
|
|
params := &model.GetErrorParams{}
|
|
|
|
serviceName := r.URL.Query().Get("serviceName")
|
|
if len(serviceName) != 0 {
|
|
params.ServiceName = serviceName
|
|
}
|
|
|
|
errorType := r.URL.Query().Get("errorType")
|
|
if len(errorType) != 0 {
|
|
params.ErrorType = errorType
|
|
}
|
|
|
|
errorId := r.URL.Query().Get("errorId")
|
|
if len(errorId) != 0 {
|
|
params.ErrorID = errorId
|
|
}
|
|
|
|
return params, nil
|
|
}
|
|
|
|
func parseTagFilterRequest(r *http.Request) (*model.TagFilterParams, error) {
|
|
var postData *model.TagFilterParams
|
|
err := json.NewDecoder(r.Body).Decode(&postData)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
postData.Start, err = parseTimeStr(postData.StartStr, "start")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return postData, nil
|
|
|
|
}
|
|
|
|
func parseTagValueRequest(r *http.Request) (*model.TagFilterParams, error) {
|
|
var postData *model.TagFilterParams
|
|
err := json.NewDecoder(r.Body).Decode(&postData)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if postData.TagKey == "" {
|
|
return nil, fmt.Errorf("%s param missing in query", postData.TagKey)
|
|
}
|
|
|
|
postData.Start, err = parseTimeStr(postData.StartStr, "start")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return postData, nil
|
|
|
|
}
|
|
|
|
func parseErrorsRequest(r *http.Request) (*model.GetErrorsParams, error) {
|
|
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTimeMinusBuffer("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
params := &model.GetErrorsParams{
|
|
Start: startTime,
|
|
End: endTime,
|
|
}
|
|
|
|
return params, nil
|
|
}
|
|
|
|
func fetchArrayValues(param string, r *http.Request) []string {
|
|
valueStr := r.URL.Query().Get(param)
|
|
var values []string
|
|
if len(valueStr) == 0 {
|
|
return values
|
|
}
|
|
err := json.Unmarshal([]byte(valueStr), &values)
|
|
if err != nil {
|
|
zap.S().Error("Error in parsing service params", zap.Error(err))
|
|
}
|
|
return values
|
|
}
|
|
|
|
func parseTags(param string, r *http.Request) (*[]model.TagQuery, error) {
|
|
|
|
tags := new([]model.TagQuery)
|
|
tagsStr := r.URL.Query().Get(param)
|
|
|
|
if len(tagsStr) == 0 {
|
|
return tags, nil
|
|
}
|
|
err := json.Unmarshal([]byte(tagsStr), tags)
|
|
if err != nil {
|
|
zap.S().Error("Error in parsig tags", zap.Error(err))
|
|
return nil, fmt.Errorf("error in parsing %s ", param)
|
|
}
|
|
// zap.S().Info("Tags: ", *tags)
|
|
|
|
return tags, nil
|
|
}
|
|
|
|
func parseTagsV2(param string, r *http.Request) (*[]model.TagQueryV2, error) {
|
|
|
|
tags := new([]model.TagQueryV2)
|
|
tagsStr := r.URL.Query().Get(param)
|
|
|
|
if len(tagsStr) == 0 {
|
|
return tags, nil
|
|
}
|
|
err := json.Unmarshal([]byte(tagsStr), tags)
|
|
if err != nil {
|
|
zap.S().Error("Error in parsig tags", zap.Error(err))
|
|
return nil, fmt.Errorf("error in parsing %s ", param)
|
|
}
|
|
// zap.S().Info("Tags: ", *tags)
|
|
|
|
return tags, nil
|
|
}
|
|
|
|
func parseApplicationPercentileRequest(r *http.Request) (*model.ApplicationPercentileParams, error) {
|
|
|
|
startTime, err := parseTime("start", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endTime, err := parseTime("end", r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stepStr := r.URL.Query().Get("step")
|
|
if len(stepStr) == 0 {
|
|
return nil, errors.New("step param missing in query")
|
|
}
|
|
|
|
serviceName := r.URL.Query().Get("service")
|
|
if len(serviceName) == 0 {
|
|
return nil, errors.New("serviceName param missing in query")
|
|
}
|
|
|
|
startTimeStr := startTime.Format(time.RFC3339Nano)
|
|
endTimeStr := endTime.Format(time.RFC3339Nano)
|
|
|
|
params := &model.ApplicationPercentileParams{
|
|
ServiceName: serviceName,
|
|
GranOrigin: startTimeStr,
|
|
Intervals: fmt.Sprintf("%s/%s", startTimeStr, endTimeStr),
|
|
}
|
|
|
|
stepInt, err := strconv.Atoi(stepStr)
|
|
if err != nil {
|
|
return nil, errors.New("step param is not in correct format")
|
|
}
|
|
|
|
params.SetGranPeriod(stepInt)
|
|
|
|
return params, nil
|
|
|
|
}
|
|
|
|
func parseTimeStr(timeStr string, param string) (*time.Time, error) {
|
|
|
|
if len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param missing in query", param)
|
|
}
|
|
|
|
timeUnix, err := strconv.ParseInt(timeStr, 10, 64)
|
|
if err != nil || len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param is not in correct timestamp format", param)
|
|
}
|
|
|
|
timeFmt := time.Unix(0, timeUnix)
|
|
|
|
return &timeFmt, nil
|
|
|
|
}
|
|
|
|
func parseTimeMinusBufferStr(timeStr string, param string) (*time.Time, error) {
|
|
|
|
if len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param missing in query", param)
|
|
}
|
|
|
|
timeUnix, err := strconv.ParseInt(timeStr, 10, 64)
|
|
if err != nil || len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param is not in correct timestamp format", param)
|
|
}
|
|
|
|
timeUnixNow := time.Now().UnixNano()
|
|
if timeUnix > timeUnixNow-30000000000 {
|
|
timeUnix = timeUnix - 30000000000
|
|
}
|
|
|
|
timeFmt := time.Unix(0, timeUnix)
|
|
|
|
return &timeFmt, nil
|
|
|
|
}
|
|
|
|
func parseTime(param string, r *http.Request) (*time.Time, error) {
|
|
|
|
timeStr := r.URL.Query().Get(param)
|
|
if len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param missing in query", param)
|
|
}
|
|
|
|
timeUnix, err := strconv.ParseInt(timeStr, 10, 64)
|
|
if err != nil || len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param is not in correct timestamp format", param)
|
|
}
|
|
|
|
timeFmt := time.Unix(0, timeUnix)
|
|
|
|
return &timeFmt, nil
|
|
|
|
}
|
|
|
|
func parseTimeMinusBuffer(param string, r *http.Request) (*time.Time, error) {
|
|
|
|
timeStr := r.URL.Query().Get(param)
|
|
if len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param missing in query", param)
|
|
}
|
|
|
|
timeUnix, err := strconv.ParseInt(timeStr, 10, 64)
|
|
if err != nil || len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param is not in correct timestamp format", param)
|
|
}
|
|
|
|
timeUnixNow := time.Now().UnixNano()
|
|
if timeUnix > timeUnixNow-30000000000 {
|
|
timeUnix = timeUnix - 30000000000
|
|
}
|
|
|
|
timeFmt := time.Unix(0, timeUnix)
|
|
|
|
return &timeFmt, nil
|
|
|
|
}
|
|
|
|
func parseTimestamp(param string, r *http.Request) (*string, error) {
|
|
timeStr := r.URL.Query().Get(param)
|
|
if len(timeStr) == 0 {
|
|
return nil, fmt.Errorf("%s param missing in query", param)
|
|
}
|
|
|
|
// timeUnix, err := strconv.ParseInt(timeStr, 10, 64)
|
|
// if err != nil || len(timeStr) == 0 {
|
|
// return nil, fmt.Errorf("%s param is not in correct timestamp format", param)
|
|
// }
|
|
|
|
return &timeStr, nil
|
|
|
|
}
|
|
func parseSetRulesRequest(r *http.Request) (string, *model.ApiError) {
|
|
|
|
return "", nil
|
|
}
|
|
|
|
func parseTTLParams(r *http.Request) (*model.TTLParams, error) {
|
|
|
|
// make sure either of the query params are present
|
|
typeTTL := r.URL.Query().Get("type")
|
|
delDuration := r.URL.Query().Get("duration")
|
|
coldStorage := r.URL.Query().Get("coldStorage")
|
|
toColdDuration := r.URL.Query().Get("toColdDuration")
|
|
|
|
if len(typeTTL) == 0 || len(delDuration) == 0 {
|
|
return nil, fmt.Errorf("type and duration param cannot be empty from the query")
|
|
}
|
|
|
|
// Validate the type parameter
|
|
if typeTTL != constants.TraceTTL && typeTTL != constants.MetricsTTL {
|
|
return nil, fmt.Errorf("type param should be <metrics|traces>, got %v", typeTTL)
|
|
}
|
|
|
|
// Validate the TTL duration.
|
|
durationParsed, err := time.ParseDuration(delDuration)
|
|
if err != nil || durationParsed.Seconds() <= 0 {
|
|
return nil, fmt.Errorf("Not a valid TTL duration %v", delDuration)
|
|
}
|
|
|
|
var toColdParsed time.Duration
|
|
|
|
// If some cold storage is provided, validate the cold storage move TTL.
|
|
if len(coldStorage) > 0 {
|
|
toColdParsed, err = time.ParseDuration(toColdDuration)
|
|
if err != nil || toColdParsed.Seconds() <= 0 {
|
|
return nil, fmt.Errorf("Not a valid toCold TTL duration %v", toColdDuration)
|
|
}
|
|
if toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() {
|
|
return nil, fmt.Errorf("Delete TTL should be greater than cold storage move TTL.")
|
|
}
|
|
}
|
|
|
|
return &model.TTLParams{
|
|
Type: typeTTL,
|
|
DelDuration: durationParsed.Seconds(),
|
|
ColdStorageVolume: coldStorage,
|
|
ToColdStorageDuration: toColdParsed.Seconds(),
|
|
}, nil
|
|
}
|
|
|
|
func parseGetTTL(r *http.Request) (*model.GetTTLParams, error) {
|
|
|
|
typeTTL := r.URL.Query().Get("type")
|
|
getAllTTL := false
|
|
|
|
if len(typeTTL) == 0 {
|
|
getAllTTL = true
|
|
} else {
|
|
// Validate the type parameter
|
|
if typeTTL != constants.TraceTTL && typeTTL != constants.MetricsTTL {
|
|
return nil, fmt.Errorf("type param should be <metrics|traces>, got %v", typeTTL)
|
|
}
|
|
}
|
|
|
|
return &model.GetTTLParams{Type: typeTTL, GetAllTTL: getAllTTL}, nil
|
|
}
|
|
|
|
func parseRemoveTTL(r *http.Request) (*model.RemoveTTLParams, error) {
|
|
|
|
typeTTL := r.URL.Query().Get("type")
|
|
removeAllTTL := false
|
|
|
|
if len(typeTTL) == 0 {
|
|
removeAllTTL = true
|
|
} else {
|
|
// Validate the type parameter
|
|
if typeTTL != constants.TraceTTL && typeTTL != constants.MetricsTTL {
|
|
return nil, fmt.Errorf("type param should be <metrics|traces>, got %v", typeTTL)
|
|
}
|
|
}
|
|
|
|
return &model.RemoveTTLParams{Type: typeTTL, RemoveAllTTL: removeAllTTL}, nil
|
|
}
|
|
|
|
func parseUserPreferences(r *http.Request) (*model.UserPreferences, error) {
|
|
|
|
var userPreferences model.UserPreferences
|
|
err := json.NewDecoder(r.Body).Decode(&userPreferences)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &userPreferences, nil
|
|
|
|
}
|