Skip to content

Commit 9495b73

Browse files
authored
feat(ui): Add support for endpoints[].ui.hide-errors to hide errors (#1361)
Supersedes #1292
1 parent c8bdecb commit 9495b73

File tree

5 files changed

+182
-16
lines changed

5 files changed

+182
-16
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ You can then configure alerts to be triggered when an endpoint is unhealthy once
309309
| `endpoints[].ui.hide-hostname` | Whether to hide the hostname from the results. | `false` |
310310
| `endpoints[].ui.hide-port` | Whether to hide the port from the results. | `false` |
311311
| `endpoints[].ui.hide-url` | Whether to hide the URL from the results. Useful if the URL contains a token. | `false` |
312+
| `endpoints[].ui.hide-errors` | Whether to hide errors from the results. | `false` |
312313
| `endpoints[].ui.dont-resolve-failed-conditions` | Whether to resolve failed conditions for the UI. | `false` |
313314
| `endpoints[].ui.badge.response-time` | List of response time thresholds. Each time a threshold is reached, the badge has a different color. | `[50, 200, 300, 500, 750]` |
314315
| `endpoints[].extra-labels` | Extra labels to add to the metrics. Useful for grouping endpoints together. | `{}` |

client/client_test.go

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ func TestPing(t *testing.T) {
131131

132132
func TestCanPerformStartTLS(t *testing.T) {
133133
type args struct {
134-
address string
135-
insecure bool
134+
address string
135+
insecure bool
136136
dnsresolver string
137137
}
138138
tests := []struct {
@@ -168,7 +168,7 @@ func TestCanPerformStartTLS(t *testing.T) {
168168
{
169169
name: "dns resolver",
170170
args: args{
171-
address: "smtp.gmail.com:587",
171+
address: "smtp.gmail.com:587",
172172
dnsresolver: "tcp://1.1.1.1:53",
173173
},
174174
wantConnected: true,
@@ -340,7 +340,7 @@ func TestQueryWebSocket(t *testing.T) {
340340
}
341341

342342
func TestTlsRenegotiation(t *testing.T) {
343-
tests := []struct {
343+
scenarios := []struct {
344344
name string
345345
cfg TLSConfig
346346
expectedConfig tls.RenegotiationSupport
@@ -371,12 +371,12 @@ func TestTlsRenegotiation(t *testing.T) {
371371
expectedConfig: tls.RenegotiateNever,
372372
},
373373
}
374-
for _, test := range tests {
375-
t.Run(test.name, func(t *testing.T) {
374+
for _, scenario := range scenarios {
375+
t.Run(scenario.name, func(t *testing.T) {
376376
tls := &tls.Config{}
377-
tlsConfig := configureTLS(tls, test.cfg)
378-
if tlsConfig.Renegotiation != test.expectedConfig {
379-
t.Errorf("expected tls renegotiation to be %v, but got %v", test.expectedConfig, tls.Renegotiation)
377+
tlsConfig := configureTLS(tls, scenario.cfg)
378+
if tlsConfig.Renegotiation != scenario.expectedConfig {
379+
t.Errorf("expected tls renegotiation to be %v, but got %v", scenario.expectedConfig, tls.Renegotiation)
380380
}
381381
})
382382
}
@@ -513,35 +513,28 @@ func TestQueryDNS(t *testing.T) {
513513

514514
func TestCheckSSHBanner(t *testing.T) {
515515
cfg := &Config{Timeout: 3}
516-
517516
t.Run("no-auth-ssh", func(t *testing.T) {
518517
connected, status, err := CheckSSHBanner("tty.sdf.org", cfg)
519-
520518
if err != nil {
521519
t.Errorf("Expected: error != nil, got: %v ", err)
522520
}
523-
524521
if connected == false {
525522
t.Errorf("Expected: connected == true, got: %v", connected)
526523
}
527524
if status != 0 {
528525
t.Errorf("Expected: 0, got: %v", status)
529526
}
530527
})
531-
532528
t.Run("invalid-address", func(t *testing.T) {
533529
connected, status, err := CheckSSHBanner("idontplaytheodds.com", cfg)
534-
535530
if err == nil {
536531
t.Errorf("Expected: error, got: %v ", err)
537532
}
538-
539533
if connected != false {
540534
t.Errorf("Expected: connected == false, got: %v", connected)
541535
}
542536
if status != 1 {
543537
t.Errorf("Expected: 1, got: %v", status)
544538
}
545539
})
546-
547540
}

config/endpoint/endpoint.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ func (e *Endpoint) EvaluateHealthWithContext(context *gontext.Gontext) *Result {
353353
}
354354
result.port = ""
355355
}
356+
if processedEndpoint.UIConfig.HideErrors {
357+
result.Errors = nil
358+
}
356359
if processedEndpoint.UIConfig.HideConditions {
357360
result.ConditionResults = nil
358361
}

config/endpoint/endpoint_test.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,3 +1448,168 @@ func TestEndpoint_preprocessWithContext(t *testing.T) {
14481448
})
14491449
}
14501450
}
1451+
1452+
func TestEndpoint_HideUIFeatures(t *testing.T) {
1453+
defer client.InjectHTTPClient(nil)
1454+
tests := []struct {
1455+
name string
1456+
endpoint Endpoint
1457+
mockResponse test.MockRoundTripper
1458+
checkHostname bool
1459+
expectHostname string
1460+
checkErrors bool
1461+
expectErrors bool
1462+
checkConditions bool
1463+
expectConditions bool
1464+
checkErrorContent string
1465+
}{
1466+
{
1467+
name: "hide-conditions",
1468+
endpoint: Endpoint{
1469+
Name: "test-endpoint",
1470+
URL: "https://example.com/health",
1471+
Conditions: []Condition{"[STATUS] == 200", "[BODY].status == UP"},
1472+
UIConfig: &ui.Config{HideConditions: true},
1473+
},
1474+
mockResponse: test.MockRoundTripper(func(r *http.Request) *http.Response {
1475+
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(`{"status": "UP"}`))}
1476+
}),
1477+
checkConditions: true,
1478+
expectConditions: false,
1479+
},
1480+
{
1481+
name: "hide-hostname",
1482+
endpoint: Endpoint{
1483+
Name: "test-endpoint",
1484+
URL: "https://example.com/health",
1485+
Conditions: []Condition{"[STATUS] == 200"},
1486+
UIConfig: &ui.Config{HideHostname: true},
1487+
},
1488+
mockResponse: test.MockRoundTripper(func(r *http.Request) *http.Response {
1489+
return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}
1490+
}),
1491+
checkHostname: true,
1492+
expectHostname: "",
1493+
},
1494+
{
1495+
name: "hide-url-in-errors",
1496+
endpoint: Endpoint{
1497+
Name: "test-endpoint",
1498+
URL: "https://example.com/health",
1499+
Conditions: []Condition{"[CONNECTED] == true"},
1500+
UIConfig: &ui.Config{HideURL: true},
1501+
ClientConfig: &client.Config{Timeout: time.Millisecond},
1502+
},
1503+
mockResponse: nil,
1504+
checkErrors: true,
1505+
expectErrors: true,
1506+
checkErrorContent: "<redacted>",
1507+
},
1508+
{
1509+
name: "hide-port-in-errors",
1510+
endpoint: Endpoint{
1511+
Name: "test-endpoint",
1512+
URL: "https://example.com:9999/health",
1513+
Conditions: []Condition{"[CONNECTED] == true"},
1514+
UIConfig: &ui.Config{HidePort: true},
1515+
ClientConfig: &client.Config{Timeout: time.Millisecond},
1516+
},
1517+
mockResponse: nil,
1518+
checkErrors: true,
1519+
expectErrors: true,
1520+
checkErrorContent: "<redacted>",
1521+
},
1522+
{
1523+
name: "hide-errors",
1524+
endpoint: Endpoint{
1525+
Name: "test-endpoint",
1526+
URL: "https://example.com/health",
1527+
Conditions: []Condition{"[CONNECTED] == true"},
1528+
UIConfig: &ui.Config{HideErrors: true},
1529+
ClientConfig: &client.Config{Timeout: time.Millisecond},
1530+
},
1531+
mockResponse: nil,
1532+
checkErrors: true,
1533+
expectErrors: false,
1534+
},
1535+
{
1536+
name: "dont-resolve-failed-conditions",
1537+
endpoint: Endpoint{
1538+
Name: "test-endpoint",
1539+
URL: "https://example.com/health",
1540+
Conditions: []Condition{"[STATUS] == 200"},
1541+
UIConfig: &ui.Config{DontResolveFailedConditions: true},
1542+
},
1543+
mockResponse: test.MockRoundTripper(func(r *http.Request) *http.Response {
1544+
return &http.Response{StatusCode: http.StatusBadGateway, Body: http.NoBody}
1545+
}),
1546+
checkConditions: true,
1547+
expectConditions: true,
1548+
},
1549+
{
1550+
name: "multiple-hide-features",
1551+
endpoint: Endpoint{
1552+
Name: "test-endpoint",
1553+
URL: "https://example.com/health",
1554+
Conditions: []Condition{"[STATUS] == 200"},
1555+
UIConfig: &ui.Config{HideConditions: true, HideHostname: true, HideErrors: true},
1556+
},
1557+
mockResponse: test.MockRoundTripper(func(r *http.Request) *http.Response {
1558+
return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}
1559+
}),
1560+
checkConditions: true,
1561+
expectConditions: false,
1562+
checkHostname: true,
1563+
expectHostname: "",
1564+
checkErrors: true,
1565+
expectErrors: false,
1566+
},
1567+
}
1568+
for _, tt := range tests {
1569+
t.Run(tt.name, func(t *testing.T) {
1570+
if tt.mockResponse != nil {
1571+
mockClient := &http.Client{Transport: tt.mockResponse}
1572+
if tt.endpoint.ClientConfig != nil && tt.endpoint.ClientConfig.Timeout > 0 {
1573+
mockClient.Timeout = tt.endpoint.ClientConfig.Timeout
1574+
}
1575+
client.InjectHTTPClient(mockClient)
1576+
} else {
1577+
client.InjectHTTPClient(nil)
1578+
}
1579+
err := tt.endpoint.ValidateAndSetDefaults()
1580+
if err != nil {
1581+
t.Fatalf("ValidateAndSetDefaults failed: %v", err)
1582+
}
1583+
result := tt.endpoint.EvaluateHealth()
1584+
if tt.checkHostname {
1585+
if result.Hostname != tt.expectHostname {
1586+
t.Errorf("Expected hostname '%s', got '%s'", tt.expectHostname, result.Hostname)
1587+
}
1588+
}
1589+
if tt.checkErrors {
1590+
hasErrors := len(result.Errors) > 0
1591+
if hasErrors != tt.expectErrors {
1592+
t.Errorf("Expected errors=%v, got errors=%v (actual errors: %v)", tt.expectErrors, hasErrors, result.Errors)
1593+
}
1594+
if tt.checkErrorContent != "" && len(result.Errors) > 0 {
1595+
found := false
1596+
for _, err := range result.Errors {
1597+
if strings.Contains(err, tt.checkErrorContent) {
1598+
found = true
1599+
break
1600+
}
1601+
}
1602+
if !found {
1603+
t.Errorf("Expected error to contain '%s', but got: %v", tt.checkErrorContent, result.Errors)
1604+
}
1605+
}
1606+
}
1607+
if tt.checkConditions {
1608+
hasConditions := result.ConditionResults != nil && len(result.ConditionResults) > 0
1609+
if hasConditions != tt.expectConditions {
1610+
t.Errorf("Expected conditions=%v, got conditions=%v (actual: %v)", tt.expectConditions, hasConditions, result.ConditionResults)
1611+
}
1612+
}
1613+
})
1614+
}
1615+
}

config/endpoint/ui/ui.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ type Config struct {
1616
// HidePort whether to hide the port in the Result
1717
HidePort bool `yaml:"hide-port"`
1818

19+
// HideErrors whether to hide the errors in the Result
20+
HideErrors bool `yaml:"hide-errors"`
21+
1922
// DontResolveFailedConditions whether to resolve failed conditions in the Result for display in the UI
2023
DontResolveFailedConditions bool `yaml:"dont-resolve-failed-conditions"`
2124

@@ -58,6 +61,7 @@ func GetDefaultConfig() *Config {
5861
HideHostname: false,
5962
HideURL: false,
6063
HidePort: false,
64+
HideErrors: false,
6165
DontResolveFailedConditions: false,
6266
HideConditions: false,
6367
Badge: &Badge{

0 commit comments

Comments
 (0)