mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-23 02:17:11 +00:00
This reverts commit 396e0cdc2d7cf58814295f34e7efaf9f845931bc.
This commit is contained in:
parent
87ce197631
commit
382d9d4a87
@ -1,503 +0,0 @@
|
|||||||
package thirdpartyapi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/SigNoz/signoz/pkg/types/thirdpartyapitypes"
|
|
||||||
"net"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
||||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
urlPathKeyLegacy = "http.url"
|
|
||||||
serverAddressKeyLegacy = "net.peer.name"
|
|
||||||
|
|
||||||
urlPathKey = "url.full"
|
|
||||||
serverAddressKey = "server.address"
|
|
||||||
)
|
|
||||||
|
|
||||||
var defaultStepInterval = 60 * time.Second
|
|
||||||
|
|
||||||
type SemconvFieldMapping struct {
|
|
||||||
LegacyField string
|
|
||||||
CurrentField string
|
|
||||||
FieldType telemetrytypes.FieldDataType
|
|
||||||
Context telemetrytypes.FieldContext
|
|
||||||
}
|
|
||||||
|
|
||||||
var dualSemconvGroupByKeys = map[string][]qbtypes.GroupByKey{
|
|
||||||
"server": {
|
|
||||||
{
|
|
||||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
|
||||||
Name: serverAddressKey,
|
|
||||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
||||||
FieldContext: telemetrytypes.FieldContextAttribute,
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
|
||||||
Name: serverAddressKeyLegacy,
|
|
||||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
||||||
FieldContext: telemetrytypes.FieldContextAttribute,
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
{
|
|
||||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
|
||||||
Name: urlPathKey,
|
|
||||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
||||||
FieldContext: telemetrytypes.FieldContextAttribute,
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
|
||||||
Name: urlPathKeyLegacy,
|
|
||||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
||||||
FieldContext: telemetrytypes.FieldContextAttribute,
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeSemconvColumns(result *qbtypes.QueryRangeResponse) *qbtypes.QueryRangeResponse {
|
|
||||||
if result == nil || result.Data.Results == nil {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, res := range result.Data.Results {
|
|
||||||
scalarData, ok := res.(*qbtypes.ScalarData)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
serverAddressKeyIdx := -1
|
|
||||||
serverAddressKeyLegacyIdx := -1
|
|
||||||
|
|
||||||
for i, col := range scalarData.Columns {
|
|
||||||
if col.Name == serverAddressKey {
|
|
||||||
serverAddressKeyIdx = i
|
|
||||||
} else if col.Name == serverAddressKeyLegacy {
|
|
||||||
serverAddressKeyLegacyIdx = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if serverAddressKeyIdx == -1 || serverAddressKeyLegacyIdx == -1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var newRows [][]any
|
|
||||||
for _, row := range scalarData.Data {
|
|
||||||
if len(row) <= serverAddressKeyIdx || len(row) <= serverAddressKeyLegacyIdx {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverName any
|
|
||||||
if isValidValue(row[serverAddressKeyIdx]) {
|
|
||||||
serverName = row[serverAddressKeyIdx]
|
|
||||||
} else if isValidValue(row[serverAddressKeyLegacyIdx]) {
|
|
||||||
serverName = row[serverAddressKeyLegacyIdx]
|
|
||||||
}
|
|
||||||
|
|
||||||
if serverName != nil {
|
|
||||||
newRow := make([]any, len(row)-1)
|
|
||||||
newRow[0] = serverName
|
|
||||||
|
|
||||||
targetIdx := 1
|
|
||||||
for i, val := range row {
|
|
||||||
if i != serverAddressKeyLegacyIdx && i != serverAddressKeyIdx {
|
|
||||||
if targetIdx < len(newRow) {
|
|
||||||
newRow[targetIdx] = val
|
|
||||||
targetIdx++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newRows = append(newRows, newRow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newColumns := make([]*qbtypes.ColumnDescriptor, len(scalarData.Columns)-1)
|
|
||||||
targetIdx := 0
|
|
||||||
for i, col := range scalarData.Columns {
|
|
||||||
if i == serverAddressKeyIdx {
|
|
||||||
newCol := &qbtypes.ColumnDescriptor{
|
|
||||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
|
||||||
Name: serverAddressKeyLegacy,
|
|
||||||
FieldDataType: col.FieldDataType,
|
|
||||||
FieldContext: col.FieldContext,
|
|
||||||
Signal: col.Signal,
|
|
||||||
},
|
|
||||||
QueryName: col.QueryName,
|
|
||||||
AggregationIndex: col.AggregationIndex,
|
|
||||||
Meta: col.Meta,
|
|
||||||
Type: col.Type,
|
|
||||||
}
|
|
||||||
newColumns[targetIdx] = newCol
|
|
||||||
targetIdx++
|
|
||||||
} else if i != serverAddressKeyLegacyIdx {
|
|
||||||
newColumns[targetIdx] = col
|
|
||||||
targetIdx++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scalarData.Columns = newColumns
|
|
||||||
scalarData.Data = newRows
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidValue(val any) bool {
|
|
||||||
if val == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if str, ok := val.(string); ok {
|
|
||||||
return str != "" && str != "n/a"
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func FilterResponse(results []*qbtypes.QueryRangeResponse) []*qbtypes.QueryRangeResponse {
|
|
||||||
filteredResults := make([]*qbtypes.QueryRangeResponse, 0, len(results))
|
|
||||||
|
|
||||||
for _, res := range results {
|
|
||||||
if res.Data.Results == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredData := make([]any, 0, len(res.Data.Results))
|
|
||||||
for _, result := range res.Data.Results {
|
|
||||||
if result == nil {
|
|
||||||
filteredData = append(filteredData, result)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch resultData := result.(type) {
|
|
||||||
case *qbtypes.TimeSeriesData:
|
|
||||||
if resultData.Aggregations != nil {
|
|
||||||
for _, agg := range resultData.Aggregations {
|
|
||||||
filteredSeries := make([]*qbtypes.TimeSeries, 0, len(agg.Series))
|
|
||||||
for _, series := range agg.Series {
|
|
||||||
if shouldIncludeSeries(series) {
|
|
||||||
filteredSeries = append(filteredSeries, series)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
agg.Series = filteredSeries
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *qbtypes.RawData:
|
|
||||||
filteredRows := make([]*qbtypes.RawRow, 0, len(resultData.Rows))
|
|
||||||
for _, row := range resultData.Rows {
|
|
||||||
if shouldIncludeRow(row) {
|
|
||||||
filteredRows = append(filteredRows, row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultData.Rows = filteredRows
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredData = append(filteredData, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
res.Data.Results = filteredData
|
|
||||||
filteredResults = append(filteredResults, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredResults
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldIncludeSeries(series *qbtypes.TimeSeries) bool {
|
|
||||||
for _, label := range series.Labels {
|
|
||||||
if label.Key.Name == serverAddressKeyLegacy || label.Key.Name == serverAddressKey {
|
|
||||||
if strVal, ok := label.Value.(string); ok {
|
|
||||||
if net.ParseIP(strVal) != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldIncludeRow(row *qbtypes.RawRow) bool {
|
|
||||||
if row.Data != nil {
|
|
||||||
for _, key := range []string{serverAddressKeyLegacy, serverAddressKey} {
|
|
||||||
if domainVal, ok := row.Data[key]; ok {
|
|
||||||
if domainStr, ok := domainVal.(string); ok {
|
|
||||||
if net.ParseIP(domainStr) != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func containsKindStringOverride(expression string) bool {
|
|
||||||
kindStringPattern := regexp.MustCompile(`kind_string\s*[!=<>]+`)
|
|
||||||
return kindStringPattern.MatchString(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeGroupBy(base, additional []qbtypes.GroupByKey) []qbtypes.GroupByKey {
|
|
||||||
return append(base, additional...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BuildDomainList(req *thirdpartyapitypes.ThirdPartyApiRequest) (*qbtypes.QueryRangeRequest, error) {
|
|
||||||
if err := req.Validate(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
queries := []qbtypes.QueryEnvelope{
|
|
||||||
buildEndpointsQuery(req),
|
|
||||||
buildLastSeenQuery(req),
|
|
||||||
buildRpsQuery(req),
|
|
||||||
buildErrorQuery(req),
|
|
||||||
buildTotalSpanQuery(req),
|
|
||||||
buildP99Query(req),
|
|
||||||
buildErrorRateFormula(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return &qbtypes.QueryRangeRequest{
|
|
||||||
SchemaVersion: "v5",
|
|
||||||
Start: req.Start,
|
|
||||||
End: req.End,
|
|
||||||
RequestType: qbtypes.RequestTypeScalar,
|
|
||||||
CompositeQuery: qbtypes.CompositeQuery{
|
|
||||||
Queries: queries,
|
|
||||||
},
|
|
||||||
FormatOptions: &qbtypes.FormatOptions{
|
|
||||||
FormatTableResultForUI: true,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func BuildDomainInfo(req *thirdpartyapitypes.ThirdPartyApiRequest) (*qbtypes.QueryRangeRequest, error) {
|
|
||||||
if err := req.Validate(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
queries := []qbtypes.QueryEnvelope{
|
|
||||||
buildEndpointsInfoQuery(req),
|
|
||||||
buildP99InfoQuery(req),
|
|
||||||
buildErrorRateInfoQuery(req),
|
|
||||||
buildLastSeenInfoQuery(req),
|
|
||||||
}
|
|
||||||
|
|
||||||
return &qbtypes.QueryRangeRequest{
|
|
||||||
SchemaVersion: "v5",
|
|
||||||
Start: req.Start,
|
|
||||||
End: req.End,
|
|
||||||
RequestType: qbtypes.RequestTypeScalar,
|
|
||||||
CompositeQuery: qbtypes.CompositeQuery{
|
|
||||||
Queries: queries,
|
|
||||||
},
|
|
||||||
FormatOptions: &qbtypes.FormatOptions{
|
|
||||||
FormatTableResultForUI: true,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildEndpointsQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "endpoints",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "count_distinct(http.url)"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: mergeGroupBy(dualSemconvGroupByKeys["server"], req.GroupBy),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildLastSeenQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "lastseen",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "max(timestamp)"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: mergeGroupBy(dualSemconvGroupByKeys["server"], req.GroupBy),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildRpsQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "rps",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "rate()"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: mergeGroupBy(dualSemconvGroupByKeys["server"], req.GroupBy),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildErrorQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "error",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "count()"},
|
|
||||||
},
|
|
||||||
Filter: buildErrorFilter(req.Filter),
|
|
||||||
GroupBy: mergeGroupBy(dualSemconvGroupByKeys["server"], req.GroupBy),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildTotalSpanQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "total_span",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "count()"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: mergeGroupBy(dualSemconvGroupByKeys["server"], req.GroupBy),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildP99Query(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "p99",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "p99(duration_nano)"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: mergeGroupBy(dualSemconvGroupByKeys["server"], req.GroupBy),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildErrorRateFormula() qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeFormula,
|
|
||||||
Spec: qbtypes.QueryBuilderFormula{
|
|
||||||
Name: "error_rate",
|
|
||||||
Expression: "(error/total_span)*100",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildEndpointsInfoQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "endpoints",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "rate(http.url)"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: mergeGroupBy(dualSemconvGroupByKeys["url"], req.GroupBy),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildP99InfoQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "p99",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "p99(duration_nano)"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: req.GroupBy,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildErrorRateInfoQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "error_rate",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "rate()"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: req.GroupBy,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildLastSeenInfoQuery(req *thirdpartyapitypes.ThirdPartyApiRequest) qbtypes.QueryEnvelope {
|
|
||||||
return qbtypes.QueryEnvelope{
|
|
||||||
Type: qbtypes.QueryTypeBuilder,
|
|
||||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
|
||||||
Name: "lastseen",
|
|
||||||
Signal: telemetrytypes.SignalTraces,
|
|
||||||
StepInterval: qbtypes.Step{Duration: defaultStepInterval},
|
|
||||||
Aggregations: []qbtypes.TraceAggregation{
|
|
||||||
{Expression: "max(timestamp)"},
|
|
||||||
},
|
|
||||||
Filter: buildBaseFilter(req.Filter),
|
|
||||||
GroupBy: req.GroupBy,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildBaseFilter(additionalFilter *qbtypes.Filter) *qbtypes.Filter {
|
|
||||||
baseExpression := fmt.Sprintf("(%s EXISTS OR %s EXISTS) AND kind_string = 'Client'",
|
|
||||||
urlPathKeyLegacy, urlPathKey)
|
|
||||||
|
|
||||||
if additionalFilter != nil && additionalFilter.Expression != "" {
|
|
||||||
if containsKindStringOverride(additionalFilter.Expression) {
|
|
||||||
return &qbtypes.Filter{Expression: baseExpression}
|
|
||||||
}
|
|
||||||
baseExpression = fmt.Sprintf("(%s) AND (%s)", baseExpression, additionalFilter.Expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &qbtypes.Filter{Expression: baseExpression}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildErrorFilter(additionalFilter *qbtypes.Filter) *qbtypes.Filter {
|
|
||||||
errorExpression := fmt.Sprintf("has_error = true AND (%s EXISTS OR %s EXISTS) AND kind_string = 'Client'",
|
|
||||||
urlPathKeyLegacy, urlPathKey)
|
|
||||||
|
|
||||||
if additionalFilter != nil && additionalFilter.Expression != "" {
|
|
||||||
if containsKindStringOverride(additionalFilter.Expression) {
|
|
||||||
return &qbtypes.Filter{Expression: errorExpression}
|
|
||||||
}
|
|
||||||
errorExpression = fmt.Sprintf("(%s) AND (%s)", errorExpression, additionalFilter.Expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &qbtypes.Filter{Expression: errorExpression}
|
|
||||||
}
|
|
||||||
@ -1,233 +0,0 @@
|
|||||||
package thirdpartyapi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/SigNoz/signoz/pkg/types/thirdpartyapitypes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
||||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFilterResponse(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input []*qbtypes.QueryRangeResponse
|
|
||||||
expected []*qbtypes.QueryRangeResponse
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "should filter out IP addresses from series labels",
|
|
||||||
input: []*qbtypes.QueryRangeResponse{
|
|
||||||
{
|
|
||||||
Data: qbtypes.QueryData{
|
|
||||||
Results: []any{
|
|
||||||
&qbtypes.TimeSeriesData{
|
|
||||||
Aggregations: []*qbtypes.AggregationBucket{
|
|
||||||
{
|
|
||||||
Series: []*qbtypes.TimeSeries{
|
|
||||||
{
|
|
||||||
Labels: []*qbtypes.Label{
|
|
||||||
{
|
|
||||||
Key: telemetrytypes.TelemetryFieldKey{Name: "net.peer.name"},
|
|
||||||
Value: "192.168.1.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Labels: []*qbtypes.Label{
|
|
||||||
{
|
|
||||||
Key: telemetrytypes.TelemetryFieldKey{Name: "net.peer.name"},
|
|
||||||
Value: "example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: []*qbtypes.QueryRangeResponse{
|
|
||||||
{
|
|
||||||
Data: qbtypes.QueryData{
|
|
||||||
Results: []any{
|
|
||||||
&qbtypes.TimeSeriesData{
|
|
||||||
Aggregations: []*qbtypes.AggregationBucket{
|
|
||||||
{
|
|
||||||
Series: []*qbtypes.TimeSeries{
|
|
||||||
{
|
|
||||||
Labels: []*qbtypes.Label{
|
|
||||||
{
|
|
||||||
Key: telemetrytypes.TelemetryFieldKey{Name: "net.peer.name"},
|
|
||||||
Value: "example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "should filter out IP addresses from raw data",
|
|
||||||
input: []*qbtypes.QueryRangeResponse{
|
|
||||||
{
|
|
||||||
Data: qbtypes.QueryData{
|
|
||||||
Results: []any{
|
|
||||||
&qbtypes.RawData{
|
|
||||||
Rows: []*qbtypes.RawRow{
|
|
||||||
{
|
|
||||||
Data: map[string]any{
|
|
||||||
"net.peer.name": "192.168.1.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Data: map[string]any{
|
|
||||||
"net.peer.name": "example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: []*qbtypes.QueryRangeResponse{
|
|
||||||
{
|
|
||||||
Data: qbtypes.QueryData{
|
|
||||||
Results: []any{
|
|
||||||
&qbtypes.RawData{
|
|
||||||
Rows: []*qbtypes.RawRow{
|
|
||||||
{
|
|
||||||
Data: map[string]any{
|
|
||||||
"net.peer.name": "example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
result := FilterResponse(tt.input)
|
|
||||||
assert.Equal(t, tt.expected, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildDomainList(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input *thirdpartyapitypes.ThirdPartyApiRequest
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "basic domain list query",
|
|
||||||
input: &thirdpartyapitypes.ThirdPartyApiRequest{
|
|
||||||
Start: 1000,
|
|
||||||
End: 2000,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "with filters and group by",
|
|
||||||
input: &thirdpartyapitypes.ThirdPartyApiRequest{
|
|
||||||
Start: 1000,
|
|
||||||
End: 2000,
|
|
||||||
Filter: &qbtypes.Filter{
|
|
||||||
Expression: "test = 'value'",
|
|
||||||
},
|
|
||||||
GroupBy: []qbtypes.GroupByKey{
|
|
||||||
{
|
|
||||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
|
||||||
Name: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
result, err := BuildDomainList(tt.input)
|
|
||||||
if tt.wantErr {
|
|
||||||
assert.Error(t, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, result)
|
|
||||||
assert.Equal(t, tt.input.Start, result.Start)
|
|
||||||
assert.Equal(t, tt.input.End, result.End)
|
|
||||||
assert.NotNil(t, result.CompositeQuery)
|
|
||||||
assert.Len(t, result.CompositeQuery.Queries, 7) // endpoints, lastseen, rps, error, total_span, p99, error_rate
|
|
||||||
assert.Equal(t, "v5", result.SchemaVersion)
|
|
||||||
assert.Equal(t, qbtypes.RequestTypeScalar, result.RequestType)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildDomainInfo(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input *thirdpartyapitypes.ThirdPartyApiRequest
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "basic domain info query",
|
|
||||||
input: &thirdpartyapitypes.ThirdPartyApiRequest{
|
|
||||||
Start: 1000,
|
|
||||||
End: 2000,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "with filters and group by",
|
|
||||||
input: &thirdpartyapitypes.ThirdPartyApiRequest{
|
|
||||||
Start: 1000,
|
|
||||||
End: 2000,
|
|
||||||
Filter: &qbtypes.Filter{
|
|
||||||
Expression: "test = 'value'",
|
|
||||||
},
|
|
||||||
GroupBy: []qbtypes.GroupByKey{
|
|
||||||
{
|
|
||||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
|
||||||
Name: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
result, err := BuildDomainInfo(tt.input)
|
|
||||||
if tt.wantErr {
|
|
||||||
assert.Error(t, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, result)
|
|
||||||
assert.Equal(t, tt.input.Start, result.Start)
|
|
||||||
assert.Equal(t, tt.input.End, result.End)
|
|
||||||
assert.NotNil(t, result.CompositeQuery)
|
|
||||||
assert.Len(t, result.CompositeQuery.Queries, 4) // endpoints, p99, error_rate, lastseen
|
|
||||||
assert.Equal(t, "v5", result.SchemaVersion)
|
|
||||||
assert.Equal(t, qbtypes.RequestTypeScalar, result.RequestType)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,9 +8,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/thirdpartyapi"
|
|
||||||
|
|
||||||
//qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -48,6 +45,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/inframetrics"
|
"github.com/SigNoz/signoz/pkg/query-service/app/inframetrics"
|
||||||
queues2 "github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
|
queues2 "github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
|
||||||
|
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/thirdPartyApi"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/logs"
|
"github.com/SigNoz/signoz/pkg/query-service/app/logs"
|
||||||
logsv3 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v3"
|
logsv3 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v3"
|
||||||
logsv4 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v4"
|
logsv4 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v4"
|
||||||
@ -4984,130 +4982,105 @@ func (aH *APIHandler) getQueueOverview(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getDomainList(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getDomainList(w http.ResponseWriter, r *http.Request) {
|
||||||
// Extract claims from context for organization ID
|
|
||||||
claims, err := authtypes.ClaimsFromContext(r.Context())
|
claims, err := authtypes.ClaimsFromContext(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(w, err)
|
render.Error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(w, err)
|
render.Error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the request body to get third-party query parameters
|
|
||||||
thirdPartyQueryRequest, apiErr := ParseRequestBody(r)
|
thirdPartyQueryRequest, apiErr := ParseRequestBody(r)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
zap.L().Error("Failed to parse request body", zap.Error(apiErr))
|
zap.L().Error(apiErr.Err.Error())
|
||||||
render.Error(w, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, apiErr.Error()))
|
RespondError(w, apiErr, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the v5 query range request for domain listing
|
queryRangeParams, err := thirdPartyApi.BuildDomainList(thirdPartyQueryRequest)
|
||||||
queryRangeRequest, err := thirdpartyapi.BuildDomainList(thirdPartyQueryRequest)
|
if err := validateQueryRangeParamsV3(queryRangeParams); err != nil {
|
||||||
|
zap.L().Error(err.Error())
|
||||||
|
RespondError(w, apiErr, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []*v3.Result
|
||||||
|
var errQuriesByName map[string]error
|
||||||
|
|
||||||
|
result, errQuriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.L().Error("Failed to build domain list query", zap.Error(err))
|
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||||
apiErrObj := errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
RespondError(w, apiErrObj, errQuriesByName)
|
||||||
render.Error(w, apiErrObj)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the v5 query range request
|
result, err = postprocess.PostProcessResult(result, queryRangeParams)
|
||||||
if err := queryRangeRequest.Validate(); err != nil {
|
|
||||||
zap.L().Error("Query validation failed", zap.Error(err))
|
|
||||||
apiErrObj := errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
|
||||||
render.Error(w, apiErrObj)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the query using the v5 querier
|
|
||||||
result, err := aH.Signoz.Querier.QueryRange(r.Context(), orgID, queryRangeRequest)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.L().Error("Query execution failed", zap.Error(err))
|
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||||
apiErrObj := errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
RespondError(w, apiErrObj, errQuriesByName)
|
||||||
render.Error(w, apiErrObj)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result = thirdpartyapi.MergeSemconvColumns(result)
|
|
||||||
|
|
||||||
// Filter IP addresses if ShowIp is false
|
|
||||||
var finalResult = result
|
|
||||||
if !thirdPartyQueryRequest.ShowIp {
|
if !thirdPartyQueryRequest.ShowIp {
|
||||||
filteredResults := thirdpartyapi.FilterResponse([]*qbtypes.QueryRangeResponse{result})
|
result = thirdPartyApi.FilterResponse(result)
|
||||||
if len(filteredResults) > 0 {
|
|
||||||
finalResult = filteredResults[0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the response
|
resp := v3.QueryRangeResponse{
|
||||||
aH.Respond(w, finalResult)
|
Result: result,
|
||||||
|
}
|
||||||
|
aH.Respond(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDomainInfo handles requests for domain information using v5 query builder
|
|
||||||
func (aH *APIHandler) getDomainInfo(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getDomainInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
// Extract claims from context for organization ID
|
|
||||||
claims, err := authtypes.ClaimsFromContext(r.Context())
|
claims, err := authtypes.ClaimsFromContext(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(w, err)
|
render.Error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(w, err)
|
render.Error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the request body to get third-party query parameters
|
|
||||||
thirdPartyQueryRequest, apiErr := ParseRequestBody(r)
|
thirdPartyQueryRequest, apiErr := ParseRequestBody(r)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
zap.L().Error("Failed to parse request body", zap.Error(apiErr))
|
zap.L().Error(apiErr.Err.Error())
|
||||||
render.Error(w, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, apiErr.Error()))
|
RespondError(w, apiErr, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the v5 query range request for domain info
|
queryRangeParams, err := thirdPartyApi.BuildDomainInfo(thirdPartyQueryRequest)
|
||||||
queryRangeRequest, err := thirdpartyapi.BuildDomainInfo(thirdPartyQueryRequest)
|
|
||||||
|
if err := validateQueryRangeParamsV3(queryRangeParams); err != nil {
|
||||||
|
zap.L().Error(err.Error())
|
||||||
|
RespondError(w, apiErr, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []*v3.Result
|
||||||
|
var errQuriesByName map[string]error
|
||||||
|
|
||||||
|
result, errQuriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.L().Error("Failed to build domain info query", zap.Error(err))
|
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||||
apiErrObj := errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
RespondError(w, apiErrObj, errQuriesByName)
|
||||||
render.Error(w, apiErrObj)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the v5 query range request
|
result = postprocess.TransformToTableForBuilderQueries(result, queryRangeParams)
|
||||||
if err := queryRangeRequest.Validate(); err != nil {
|
|
||||||
zap.L().Error("Query validation failed", zap.Error(err))
|
|
||||||
apiErrObj := errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
|
||||||
render.Error(w, apiErrObj)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the query using the v5 querier
|
|
||||||
result, err := aH.Signoz.Querier.QueryRange(r.Context(), orgID, queryRangeRequest)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("Query execution failed", zap.Error(err))
|
|
||||||
apiErrObj := errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
|
||||||
render.Error(w, apiErrObj)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
result = thirdpartyapi.MergeSemconvColumns(result)
|
|
||||||
|
|
||||||
// Filter IP addresses if ShowIp is false
|
|
||||||
var finalResult *qbtypes.QueryRangeResponse = result
|
|
||||||
if !thirdPartyQueryRequest.ShowIp {
|
if !thirdPartyQueryRequest.ShowIp {
|
||||||
filteredResults := thirdpartyapi.FilterResponse([]*qbtypes.QueryRangeResponse{result})
|
result = thirdPartyApi.FilterResponse(result)
|
||||||
if len(filteredResults) > 0 {
|
|
||||||
finalResult = filteredResults[0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the response
|
resp := v3.QueryRangeResponse{
|
||||||
aH.Respond(w, finalResult)
|
Result: result,
|
||||||
|
}
|
||||||
|
aH.Respond(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterTraceFunnelsRoutes adds trace funnels routes
|
// RegisterTraceFunnelsRoutes adds trace funnels routes
|
||||||
|
|||||||
13
pkg/query-service/app/integrations/thirdPartyApi/model.go
Normal file
13
pkg/query-service/app/integrations/thirdPartyApi/model.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package thirdPartyApi
|
||||||
|
|
||||||
|
import v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||||
|
|
||||||
|
type ThirdPartyApis struct {
|
||||||
|
Start int64 `json:"start"`
|
||||||
|
End int64 `json:"end"`
|
||||||
|
ShowIp bool `json:"show_ip,omitempty"`
|
||||||
|
Domain int64 `json:"domain,omitempty"`
|
||||||
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
|
Filters v3.FilterSet `json:"filters,omitempty"`
|
||||||
|
GroupBy []v3.AttributeKey `json:"groupBy,omitempty"`
|
||||||
|
}
|
||||||
598
pkg/query-service/app/integrations/thirdPartyApi/translator.go
Normal file
598
pkg/query-service/app/integrations/thirdPartyApi/translator.go
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
package thirdPartyApi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
urlPathKey = "http.url"
|
||||||
|
serverNameKey = "net.peer.name"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultStepInterval int64 = 60
|
||||||
|
|
||||||
|
func FilterResponse(results []*v3.Result) []*v3.Result {
|
||||||
|
filteredResults := make([]*v3.Result, 0, len(results))
|
||||||
|
|
||||||
|
for _, res := range results {
|
||||||
|
if res.Table == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filteredRows := make([]*v3.TableRow, 0, len(res.Table.Rows))
|
||||||
|
for _, row := range res.Table.Rows {
|
||||||
|
if row.Data != nil {
|
||||||
|
if domainVal, ok := row.Data[serverNameKey]; ok {
|
||||||
|
if domainStr, ok := domainVal.(string); ok {
|
||||||
|
if net.ParseIP(domainStr) != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filteredRows = append(filteredRows, row)
|
||||||
|
}
|
||||||
|
res.Table.Rows = filteredRows
|
||||||
|
|
||||||
|
filteredResults = append(filteredResults, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredResults
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFilterSet(existingFilters []v3.FilterItem, apiFilters v3.FilterSet) []v3.FilterItem {
|
||||||
|
if len(apiFilters.Items) != 0 {
|
||||||
|
existingFilters = append(existingFilters, apiFilters.Items...)
|
||||||
|
}
|
||||||
|
return existingFilters
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroupBy(existingGroupBy []v3.AttributeKey, apiGroupBy []v3.AttributeKey) []v3.AttributeKey {
|
||||||
|
if len(apiGroupBy) != 0 {
|
||||||
|
existingGroupBy = append(existingGroupBy, apiGroupBy...)
|
||||||
|
}
|
||||||
|
return existingGroupBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildDomainList(thirdPartyApis *ThirdPartyApis) (*v3.QueryRangeParamsV3, error) {
|
||||||
|
|
||||||
|
unixMilliStart := thirdPartyApis.Start
|
||||||
|
unixMilliEnd := thirdPartyApis.End
|
||||||
|
|
||||||
|
builderQueries := make(map[string]*v3.BuilderQuery)
|
||||||
|
|
||||||
|
builderQueries["endpoints"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "endpoints",
|
||||||
|
Legend: "endpoints",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorCountDistinct,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
TimeAggregation: v3.TimeAggregationCountDistinct,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "endpoints",
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: serverNameKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.GroupBy),
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
ShiftBy: 0,
|
||||||
|
IsAnomaly: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["lastseen"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "lastseen",
|
||||||
|
Legend: "lastseen",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorMax,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: "timestamp",
|
||||||
|
},
|
||||||
|
TimeAggregation: v3.TimeAggregationMax,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "lastseen",
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: serverNameKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.GroupBy),
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
ShiftBy: 0,
|
||||||
|
IsAnomaly: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["rps"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "rps",
|
||||||
|
Legend: "rps",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorRate,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: "",
|
||||||
|
},
|
||||||
|
TimeAggregation: v3.TimeAggregationRate,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "rps",
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: serverNameKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.GroupBy),
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
ShiftBy: 0,
|
||||||
|
IsAnomaly: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["error"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "error",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: "span_id",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
TimeAggregation: v3.TimeAggregationCount,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "has_error",
|
||||||
|
DataType: v3.AttributeKeyDataTypeBool,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "true",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "error",
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: serverNameKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.GroupBy),
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
Disabled: true,
|
||||||
|
ShiftBy: 0,
|
||||||
|
IsAnomaly: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["total_span"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "total_span",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: "span_id",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
TimeAggregation: v3.TimeAggregationCount,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "http.url",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "total_span",
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: "net.peer.name",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.GroupBy),
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
Disabled: true,
|
||||||
|
ShiftBy: 0,
|
||||||
|
IsAnomaly: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["p99"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "p99",
|
||||||
|
Legend: "p99",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorP99,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: "duration_nano",
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
TimeAggregation: "p99",
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "p99",
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: serverNameKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.GroupBy),
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
ShiftBy: 0,
|
||||||
|
IsAnomaly: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["error_rate"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "error_rate",
|
||||||
|
Expression: "(error/total_span)*100",
|
||||||
|
Legend: "error_rate",
|
||||||
|
Disabled: false,
|
||||||
|
ShiftBy: 0,
|
||||||
|
IsAnomaly: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
compositeQuery := &v3.CompositeQuery{
|
||||||
|
QueryType: v3.QueryTypeBuilder,
|
||||||
|
PanelType: v3.PanelTypeTable,
|
||||||
|
FillGaps: false,
|
||||||
|
BuilderQueries: builderQueries,
|
||||||
|
}
|
||||||
|
|
||||||
|
queryRangeParams := &v3.QueryRangeParamsV3{
|
||||||
|
Start: unixMilliStart,
|
||||||
|
End: unixMilliEnd,
|
||||||
|
Step: defaultStepInterval,
|
||||||
|
CompositeQuery: compositeQuery,
|
||||||
|
Version: "v4",
|
||||||
|
FormatForWeb: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryRangeParams, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildDomainInfo(thirdPartyApis *ThirdPartyApis) (*v3.QueryRangeParamsV3, error) {
|
||||||
|
unixMilliStart := thirdPartyApis.Start
|
||||||
|
unixMilliEnd := thirdPartyApis.End
|
||||||
|
|
||||||
|
builderQueries := make(map[string]*v3.BuilderQuery)
|
||||||
|
|
||||||
|
builderQueries["endpoints"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "endpoints",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
TimeAggregation: v3.TimeAggregationRate,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "endpoints",
|
||||||
|
Disabled: false,
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.GroupBy),
|
||||||
|
Legend: "",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["p99"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "p99",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorP99,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: "duration_nano",
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
TimeAggregation: "p99",
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "p99",
|
||||||
|
Disabled: false,
|
||||||
|
Having: nil,
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{}, thirdPartyApis.GroupBy),
|
||||||
|
Legend: "",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["error_rate"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "error_rate",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorRate,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: "",
|
||||||
|
},
|
||||||
|
TimeAggregation: v3.TimeAggregationRate,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "error_rate",
|
||||||
|
Disabled: false,
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{}, thirdPartyApis.GroupBy),
|
||||||
|
Legend: "",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
}
|
||||||
|
|
||||||
|
builderQueries["lastseen"] = &v3.BuilderQuery{
|
||||||
|
QueryName: "lastseen",
|
||||||
|
DataSource: v3.DataSourceTraces,
|
||||||
|
StepInterval: defaultStepInterval,
|
||||||
|
AggregateOperator: v3.AggregateOperatorMax,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: "timestamp",
|
||||||
|
},
|
||||||
|
TimeAggregation: v3.TimeAggregationMax,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: getFilterSet([]v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: urlPathKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: false,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: v3.FilterOperatorExists,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "kind_string",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
IsColumn: true,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "Client",
|
||||||
|
},
|
||||||
|
}, thirdPartyApis.Filters),
|
||||||
|
},
|
||||||
|
Expression: "lastseen",
|
||||||
|
Disabled: false,
|
||||||
|
Having: nil,
|
||||||
|
OrderBy: nil,
|
||||||
|
GroupBy: getGroupBy([]v3.AttributeKey{}, thirdPartyApis.GroupBy),
|
||||||
|
Legend: "",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
}
|
||||||
|
|
||||||
|
compositeQuery := &v3.CompositeQuery{
|
||||||
|
QueryType: v3.QueryTypeBuilder,
|
||||||
|
PanelType: v3.PanelTypeTable,
|
||||||
|
FillGaps: false,
|
||||||
|
BuilderQueries: builderQueries,
|
||||||
|
}
|
||||||
|
|
||||||
|
queryRangeParams := &v3.QueryRangeParamsV3{
|
||||||
|
Start: unixMilliStart,
|
||||||
|
End: unixMilliEnd,
|
||||||
|
Step: defaultStepInterval,
|
||||||
|
CompositeQuery: compositeQuery,
|
||||||
|
Version: "v4",
|
||||||
|
FormatForWeb: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryRangeParams, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,267 @@
|
|||||||
|
package thirdPartyApi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilterResponse(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []*v3.Result
|
||||||
|
expected []*v3.Result
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should filter out IP addresses from net.peer.name",
|
||||||
|
input: []*v3.Result{
|
||||||
|
{
|
||||||
|
Table: &v3.Table{
|
||||||
|
Rows: []*v3.TableRow{
|
||||||
|
{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"net.peer.name": "192.168.1.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"net.peer.name": "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []*v3.Result{
|
||||||
|
{
|
||||||
|
Table: &v3.Table{
|
||||||
|
Rows: []*v3.TableRow{
|
||||||
|
{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"net.peer.name": "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should handle nil data",
|
||||||
|
input: []*v3.Result{
|
||||||
|
{
|
||||||
|
Table: &v3.Table{
|
||||||
|
Rows: []*v3.TableRow{
|
||||||
|
{
|
||||||
|
Data: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []*v3.Result{
|
||||||
|
{
|
||||||
|
Table: &v3.Table{
|
||||||
|
Rows: []*v3.TableRow{
|
||||||
|
{
|
||||||
|
Data: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := FilterResponse(tt.input)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFilterSet(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
existingFilters []v3.FilterItem
|
||||||
|
apiFilters v3.FilterSet
|
||||||
|
expected []v3.FilterItem
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should append new filters",
|
||||||
|
existingFilters: []v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{Key: "existing"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apiFilters: v3.FilterSet{
|
||||||
|
Items: []v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{Key: "new"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{Key: "existing"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{Key: "new"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should handle empty api filters",
|
||||||
|
existingFilters: []v3.FilterItem{{Key: v3.AttributeKey{Key: "existing"}}},
|
||||||
|
apiFilters: v3.FilterSet{},
|
||||||
|
expected: []v3.FilterItem{{Key: v3.AttributeKey{Key: "existing"}}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := getFilterSet(tt.existingFilters, tt.apiFilters)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupBy(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
existingGroup []v3.AttributeKey
|
||||||
|
apiGroup []v3.AttributeKey
|
||||||
|
expected []v3.AttributeKey
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should append new group by attributes",
|
||||||
|
existingGroup: []v3.AttributeKey{
|
||||||
|
{Key: "existing"},
|
||||||
|
},
|
||||||
|
apiGroup: []v3.AttributeKey{
|
||||||
|
{Key: "new"},
|
||||||
|
},
|
||||||
|
expected: []v3.AttributeKey{
|
||||||
|
{Key: "existing"},
|
||||||
|
{Key: "new"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should handle empty api group",
|
||||||
|
existingGroup: []v3.AttributeKey{{Key: "existing"}},
|
||||||
|
apiGroup: []v3.AttributeKey{},
|
||||||
|
expected: []v3.AttributeKey{{Key: "existing"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := getGroupBy(tt.existingGroup, tt.apiGroup)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildDomainList(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input *ThirdPartyApis
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic domain list query",
|
||||||
|
input: &ThirdPartyApis{
|
||||||
|
Start: 1000,
|
||||||
|
End: 2000,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with filters and group by",
|
||||||
|
input: &ThirdPartyApis{
|
||||||
|
Start: 1000,
|
||||||
|
End: 2000,
|
||||||
|
Filters: v3.FilterSet{
|
||||||
|
Items: []v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{Key: "test"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{Key: "test"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := BuildDomainList(tt.input)
|
||||||
|
if tt.wantErr {
|
||||||
|
assert.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, result)
|
||||||
|
assert.Equal(t, tt.input.Start, result.Start)
|
||||||
|
assert.Equal(t, tt.input.End, result.End)
|
||||||
|
assert.NotNil(t, result.CompositeQuery)
|
||||||
|
assert.NotNil(t, result.CompositeQuery.BuilderQueries)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildDomainInfo(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input *ThirdPartyApis
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic domain info query",
|
||||||
|
input: &ThirdPartyApis{
|
||||||
|
Start: 1000,
|
||||||
|
End: 2000,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with filters and group by",
|
||||||
|
input: &ThirdPartyApis{
|
||||||
|
Start: 1000,
|
||||||
|
End: 2000,
|
||||||
|
Filters: v3.FilterSet{
|
||||||
|
Items: []v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{Key: "test"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{Key: "test"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := BuildDomainInfo(tt.input)
|
||||||
|
if tt.wantErr {
|
||||||
|
assert.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, result)
|
||||||
|
assert.Equal(t, tt.input.Start, result.Start)
|
||||||
|
assert.Equal(t, tt.input.End, result.End)
|
||||||
|
assert.NotNil(t, result.CompositeQuery)
|
||||||
|
assert.NotNil(t, result.CompositeQuery.BuilderQueries)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/SigNoz/signoz/pkg/types/thirdpartyapitypes"
|
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
@ -15,15 +14,16 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SigNoz/govaluate"
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
|
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
|
||||||
queues2 "github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
|
queues2 "github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
|
||||||
|
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/thirdPartyApi"
|
||||||
|
|
||||||
|
"github.com/SigNoz/govaluate"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
promModel "github.com/prometheus/common/model"
|
promModel "github.com/prometheus/common/model"
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
errorsV2 "github.com/SigNoz/signoz/pkg/errors"
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics"
|
"github.com/SigNoz/signoz/pkg/query-service/app/metrics"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder"
|
"github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||||
@ -981,15 +981,10 @@ func ParseQueueBody(r *http.Request) (*queues2.QueueListRequest, *model.ApiError
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseRequestBody for third party APIs
|
// ParseRequestBody for third party APIs
|
||||||
func ParseRequestBody(r *http.Request) (*thirdpartyapitypes.ThirdPartyApiRequest, error) {
|
func ParseRequestBody(r *http.Request) (*thirdPartyApi.ThirdPartyApis, *model.ApiError) {
|
||||||
req := new(thirdpartyapitypes.ThirdPartyApiRequest)
|
thirdPartApis := new(thirdPartyApi.ThirdPartyApis)
|
||||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(thirdPartApis); err != nil {
|
||||||
return nil, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, "cannot parse the request body: %v", err)
|
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("cannot parse the request body: %v", err)}
|
||||||
}
|
}
|
||||||
|
return thirdPartApis, nil
|
||||||
if err := req.Validate(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return req, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
package thirdpartyapitypes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/SigNoz/signoz/pkg/errors"
|
|
||||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ThirdPartyApiRequest struct {
|
|
||||||
Start uint64 `json:"start"`
|
|
||||||
End uint64 `json:"end"`
|
|
||||||
ShowIp bool `json:"show_ip,omitempty"`
|
|
||||||
Domain string `json:"domain,omitempty"`
|
|
||||||
Endpoint string `json:"endpoint,omitempty"`
|
|
||||||
Filter *qbtypes.Filter `json:"filters,omitempty"`
|
|
||||||
GroupBy []qbtypes.GroupByKey `json:"groupBy,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the ThirdPartyApiRequest
|
|
||||||
func (req *ThirdPartyApiRequest) Validate() error {
|
|
||||||
if req.Start >= req.End {
|
|
||||||
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "start time must be before end time")
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Filter != nil && req.Filter.Expression == "" {
|
|
||||||
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "filter expression cannot be empty when filter is provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user