Compare commits

..

1 Commits

Author SHA1 Message Date
srikanthccv
04ec681eda fix(alerts): surface individual validation errors in API response 2026-06-17 16:41:28 +05:30
5 changed files with 75 additions and 30 deletions

View File

@@ -1,5 +1,6 @@
import { useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { Skeleton } from 'antd';
import { ENTITY_VERSION_V4 } from 'constants/app';
import { FeatureKeys } from 'constants/features';
import { PANEL_TYPES } from 'constants/queryBuilder';
@@ -123,14 +124,24 @@ function ServiceOverview({
/>
<Card data-testid="service_latency">
<GraphContainer>
<Graph
onDragSelect={onDragSelect}
widget={latencyWidget}
onClickHandler={handleGraphClick('Service')}
isQueryEnabled={isQueryEnabled}
version={ENTITY_VERSION_V4}
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
/>
{topLevelOperationsIsLoading && (
<Skeleton
style={{
height: '100%',
padding: '16px',
}}
/>
)}
{!topLevelOperationsIsLoading && (
<Graph
onDragSelect={onDragSelect}
widget={latencyWidget}
onClickHandler={handleGraphClick('Service')}
isQueryEnabled={isQueryEnabled}
version={ENTITY_VERSION_V4}
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
/>
)}
</GraphContainer>
</Card>
</>

View File

@@ -1,3 +1,4 @@
import { Skeleton } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import axios from 'axios';
import { SOMETHING_WENT_WRONG } from 'constants/api';
@@ -28,14 +29,24 @@ function TopLevelOperation({
</Typography>
) : (
<GraphContainer>
<Graph
widget={widget}
onClickHandler={handleGraphClick(opName)}
onDragSelect={onDragSelect}
isQueryEnabled={!topLevelOperationsIsLoading}
version={ENTITY_VERSION_V4}
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
/>
{topLevelOperationsIsLoading && (
<Skeleton
style={{
height: '100%',
padding: '16px',
}}
/>
)}
{!topLevelOperationsIsLoading && (
<Graph
widget={widget}
onClickHandler={handleGraphClick(opName)}
onDragSelect={onDragSelect}
isQueryEnabled={!topLevelOperationsIsLoading}
version={ENTITY_VERSION_V4}
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
/>
)}
</GraphContainer>
)}
</Card>

View File

@@ -22,7 +22,7 @@ func newConfig() factory.Config {
Agent: AgentConfig{
// we will maintain the latest version of cloud integration agent from here,
// till we automate it externally or figure out a way to validate it.
Version: "v0.0.13",
Version: "v0.0.12",
},
}
}

View File

@@ -338,6 +338,7 @@ func isValidLabelValue(v string) bool {
// validate runs during UnmarshalJSON (read + write path).
// Preserves the original pre-existing checks only so that stored rules
// continue to load without errors.
// TODO(srikanthccv): remove this once v1 is deprecated and removed
func (r *PostableRule) validate() error {
var errs []error
@@ -366,9 +367,13 @@ func (r *PostableRule) validate() error {
errs = append(errs, testTemplateParsing(r)...)
joined := errors.Join(errs...)
if joined != nil {
return errors.WrapInvalidInputf(joined, errors.CodeInvalidInput, "validation failed")
if len(errs) > 0 {
messages := make([]string, len(errs))
for i, e := range errs {
messages[i] = e.Error()
}
return errors.NewInvalidInputf(errors.CodeInvalidInput, "alert rule definition is not valid").
WithAdditional(messages...)
}
return nil
}
@@ -466,9 +471,13 @@ func (r *PostableRule) Validate() error {
errs = append(errs, testTemplateParsing(r)...)
joined := errors.Join(errs...)
if joined != nil {
return errors.WrapInvalidInputf(joined, errors.CodeInvalidInput, "validation failed")
if len(errs) > 0 {
messages := make([]string, len(errs))
for i, e := range errs {
messages[i] = e.Error()
}
return errors.NewInvalidInputf(errors.CodeInvalidInput, "alert rule is not valid").
WithAdditional(messages...)
}
return nil
}

View File

@@ -4,8 +4,23 @@ import (
"encoding/json"
"strings"
"testing"
"github.com/SigNoz/signoz/pkg/errors"
)
func errorContains(err error, substr string) bool {
j := errors.AsJSON(err)
if strings.Contains(j.Message, substr) {
return true
}
for _, e := range j.Errors {
if strings.Contains(e.Message, substr) {
return true
}
}
return false
}
// validV1Builder returns a minimal valid v1 builder rule JSON.
func validV1Builder() string {
return `{
@@ -494,7 +509,7 @@ func TestValidate_PostableRule_Common(t *testing.T) {
if tt.wantErr {
if err == nil {
t.Errorf("expected error containing %q, got nil", tt.errSubstr)
} else if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) {
} else if tt.errSubstr != "" && !errorContains(err, tt.errSubstr) {
t.Errorf("expected error containing %q, got: %v", tt.errSubstr, err)
}
} else {
@@ -687,7 +702,7 @@ func TestValidate_V1_ConditionFields(t *testing.T) {
if tt.wantErr {
if validateErr == nil {
t.Errorf("expected Validate() error containing %q, got nil", tt.errSubstr)
} else if tt.errSubstr != "" && !strings.Contains(validateErr.Error(), tt.errSubstr) {
} else if tt.errSubstr != "" && !errorContains(validateErr, tt.errSubstr) {
t.Errorf("expected error containing %q, got: %v", tt.errSubstr, validateErr)
}
} else {
@@ -1029,7 +1044,7 @@ func TestValidate_V2Alpha1(t *testing.T) {
if tt.wantErr {
if err == nil {
t.Errorf("expected error containing %q, got nil", tt.errSubstr)
} else if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) {
} else if tt.errSubstr != "" && !errorContains(err, tt.errSubstr) {
t.Errorf("expected error containing %q, got: %v", tt.errSubstr, err)
}
} else {
@@ -1337,7 +1352,7 @@ func TestValidate_MultipleErrors(t *testing.T) {
t.Fatal("expected unmarshal error for wrong version")
}
// The error should mention version
if !strings.Contains(err.Error(), "version") {
if !errorContains(err, "version") {
t.Errorf("expected error to mention version, got: %v", err)
}
})
@@ -1355,10 +1370,9 @@ func TestValidate_MultipleErrors(t *testing.T) {
if validateErr == nil {
t.Fatal("expected Validate() error")
}
errStr := validateErr.Error()
// Should contain errors for thresholds, evaluation, notificationSettings
for _, substr := range []string{"evaluation", "notificationSettings"} {
if !strings.Contains(errStr, substr) {
if !errorContains(validateErr, substr) {
t.Errorf("expected error to mention %q, got: %v", substr, validateErr)
}
}
@@ -1469,7 +1483,7 @@ func TestValidate_V2Alpha1_CumulativeEvaluation(t *testing.T) {
if tt.wantErr {
if err == nil {
t.Errorf("expected error containing %q, got nil", tt.errSubstr)
} else if !strings.Contains(err.Error(), tt.errSubstr) {
} else if !errorContains(err, tt.errSubstr) {
t.Errorf("expected error containing %q, got: %v", tt.errSubstr, err)
}
} else if err != nil {