mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 23:47:12 +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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/SigNoz/signoz/pkg/modules/thirdpartyapi"
|
||||
|
||||
//qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
@ -48,6 +45,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/inframetrics"
|
||||
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"
|
||||
logsv3 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v3"
|
||||
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) {
|
||||
// Extract claims from context for organization ID
|
||||
claims, err := authtypes.ClaimsFromContext(r.Context())
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the request body to get third-party query parameters
|
||||
thirdPartyQueryRequest, apiErr := ParseRequestBody(r)
|
||||
if apiErr != nil {
|
||||
zap.L().Error("Failed to parse request body", zap.Error(apiErr))
|
||||
render.Error(w, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, apiErr.Error()))
|
||||
zap.L().Error(apiErr.Err.Error())
|
||||
RespondError(w, apiErr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Build the v5 query range request for domain listing
|
||||
queryRangeRequest, err := thirdpartyapi.BuildDomainList(thirdPartyQueryRequest)
|
||||
queryRangeParams, 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 {
|
||||
zap.L().Error("Failed to build domain list query", zap.Error(err))
|
||||
apiErrObj := errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
||||
render.Error(w, apiErrObj)
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQuriesByName)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the v5 query range request
|
||||
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)
|
||||
result, err = postprocess.PostProcessResult(result, queryRangeParams)
|
||||
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)
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQuriesByName)
|
||||
return
|
||||
}
|
||||
|
||||
result = thirdpartyapi.MergeSemconvColumns(result)
|
||||
|
||||
// Filter IP addresses if ShowIp is false
|
||||
var finalResult = result
|
||||
if !thirdPartyQueryRequest.ShowIp {
|
||||
filteredResults := thirdpartyapi.FilterResponse([]*qbtypes.QueryRangeResponse{result})
|
||||
if len(filteredResults) > 0 {
|
||||
finalResult = filteredResults[0]
|
||||
}
|
||||
result = thirdPartyApi.FilterResponse(result)
|
||||
}
|
||||
|
||||
// Send the response
|
||||
aH.Respond(w, finalResult)
|
||||
resp := v3.QueryRangeResponse{
|
||||
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) {
|
||||
// Extract claims from context for organization ID
|
||||
claims, err := authtypes.ClaimsFromContext(r.Context())
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the request body to get third-party query parameters
|
||||
thirdPartyQueryRequest, apiErr := ParseRequestBody(r)
|
||||
if apiErr != nil {
|
||||
zap.L().Error("Failed to parse request body", zap.Error(apiErr))
|
||||
render.Error(w, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, apiErr.Error()))
|
||||
zap.L().Error(apiErr.Err.Error())
|
||||
RespondError(w, apiErr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Build the v5 query range request for domain info
|
||||
queryRangeRequest, err := thirdpartyapi.BuildDomainInfo(thirdPartyQueryRequest)
|
||||
queryRangeParams, 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 {
|
||||
zap.L().Error("Failed to build domain info query", zap.Error(err))
|
||||
apiErrObj := errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
||||
render.Error(w, apiErrObj)
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQuriesByName)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the v5 query range request
|
||||
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
|
||||
}
|
||||
result = postprocess.TransformToTableForBuilderQueries(result, queryRangeParams)
|
||||
|
||||
// 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 {
|
||||
filteredResults := thirdpartyapi.FilterResponse([]*qbtypes.QueryRangeResponse{result})
|
||||
if len(filteredResults) > 0 {
|
||||
finalResult = filteredResults[0]
|
||||
}
|
||||
result = thirdPartyApi.FilterResponse(result)
|
||||
}
|
||||
|
||||
// Send the response
|
||||
aH.Respond(w, finalResult)
|
||||
resp := v3.QueryRangeResponse{
|
||||
Result: result,
|
||||
}
|
||||
aH.Respond(w, resp)
|
||||
}
|
||||
|
||||
// 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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/SigNoz/signoz/pkg/types/thirdpartyapitypes"
|
||||
"math"
|
||||
"net/http"
|
||||
"sort"
|
||||
@ -15,15 +14,16 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/govaluate"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
|
||||
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"
|
||||
promModel "github.com/prometheus/common/model"
|
||||
"go.uber.org/multierr"
|
||||
"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/queryBuilder"
|
||||
"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
|
||||
func ParseRequestBody(r *http.Request) (*thirdpartyapitypes.ThirdPartyApiRequest, error) {
|
||||
req := new(thirdpartyapitypes.ThirdPartyApiRequest)
|
||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
||||
return nil, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, "cannot parse the request body: %v", err)
|
||||
func ParseRequestBody(r *http.Request) (*thirdPartyApi.ThirdPartyApis, *model.ApiError) {
|
||||
thirdPartApis := new(thirdPartyApi.ThirdPartyApis)
|
||||
if err := json.NewDecoder(r.Body).Decode(thirdPartApis); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("cannot parse the request body: %v", err)}
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
return thirdPartApis, 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