mirror of
https://github.com/prymitive/karma
synced 2026-05-19 04:26:41 +00:00
Merge pull request #90 from cloudflare/status-filter
Replace @silenced & @inhibited filters with @status
This commit is contained in:
2
Makefile
2
Makefile
@@ -52,7 +52,7 @@ run: $(NAME)
|
||||
COLOR_LABELS_UNIQUE="instance cluster" \
|
||||
COLOR_LABELS_STATIC="job" \
|
||||
DEBUG="$(GIN_DEBUG)" \
|
||||
FILTER_DEFAULT="@inhibited=false" \
|
||||
FILTER_DEFAULT="@status=active" \
|
||||
PORT=$(PORT) \
|
||||
./$(NAME)
|
||||
|
||||
|
||||
@@ -30,14 +30,14 @@ var Autocomplete = (function() {
|
||||
// this is used to generate quick filters for label modal
|
||||
var generateHints = function(label_key, label_val) {
|
||||
var hints = [];
|
||||
if (label_key == "@silenced") {
|
||||
if (label_key == "@status") {
|
||||
// static list of hints for @silenced label
|
||||
hints.push("@silenced=true");
|
||||
hints.push("@silenced=false");
|
||||
} else if (label_key == "@inhibited") {
|
||||
// static list of hints for @inhibited label
|
||||
hints.push("@inhibited=true");
|
||||
hints.push("@inhibited=false");
|
||||
hints.push("@status=active");
|
||||
hints.push("@status=suppressed");
|
||||
hints.push("@status=unprocessed");
|
||||
hints.push("@status!=active");
|
||||
hints.push("@status!=suppressed");
|
||||
hints.push("@status!=unprocessed");
|
||||
} else {
|
||||
// equal and non-equal hints for everything else
|
||||
hints.push(label_key + "=" + label_val);
|
||||
|
||||
@@ -223,12 +223,15 @@ input#filter:focus {
|
||||
/* make silence description smaller */
|
||||
.silence-comment {
|
||||
font-size: smaller;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 0;
|
||||
border-left-color: #18bc9c;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.silence-comment-title {
|
||||
margin-top: 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.incident .panel {
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -5,10 +5,9 @@ var Colors = (function() {
|
||||
staticColorLabels;
|
||||
|
||||
var specialLabels = {
|
||||
"@silenced: false": "label-danger",
|
||||
"@silenced: true": "label-success",
|
||||
"@inhibited: false": "label-danger",
|
||||
"@inhibited: true": "label-success"
|
||||
"@status: unprocessed": "label-default",
|
||||
"@status: active": "label-danger",
|
||||
"@status: suppressed": "label-success",
|
||||
};
|
||||
|
||||
var update = function(colorData) {
|
||||
|
||||
@@ -74,15 +74,9 @@
|
||||
<%- text %>
|
||||
</a>
|
||||
<% }) %>
|
||||
<% if (alert.inhibitedBy.length) { %>
|
||||
<%= Templates.Render('buttonLabel', {elem: 'span', elemClass: 'label label-list label-success', label: {key: '@inhibited', value: 'true', text: '@inhibited: true'}}) %>
|
||||
<% } else { %>
|
||||
<%= Templates.Render('buttonLabel', {elem: 'span', elemClass: 'label label-list label-danger', label: {key: '@inhibited', value: 'false', text: '@inhibited: false'}}) %>
|
||||
<% } %>
|
||||
<% if (alert.silencedBy.length) { %>
|
||||
<%= Templates.Render('buttonLabel', {elem: 'span', elemClass: 'label label-list label-success', label: {key: '@silenced', value: 'true', text: '@silenced: true'}}) %>
|
||||
<% } else { %>
|
||||
<%= Templates.Render('buttonLabel', {elem: 'span', elemClass: 'label label-list label-danger', label: {key: '@silenced', value: 'false', text: '@silenced: false'}}) %>
|
||||
<% var attrs = Alerts.GetLabelAttrs("@status", alert.status) %>
|
||||
<%= Templates.Render('buttonLabel', {elem: 'span', attrs: attrs, label: {key: '@status', value: alert.status, text: '@status: ' + alert.status}}) %>
|
||||
<% if (alert.silencedBy.length == 0) { %>
|
||||
<% var labels = [] %>
|
||||
<% _.each(Alerts.SortMapByKey(alert.labels), function(label) { %>
|
||||
<% labels.push(label.key + '=' + label.value) %>
|
||||
@@ -110,6 +104,9 @@
|
||||
</script>
|
||||
|
||||
<script type="application/json" id="alert-group-silence">
|
||||
<small class="silence-comment-title text-muted">
|
||||
Silenced by:
|
||||
</small>
|
||||
<% _.each(alert.silencedBy, function(silenceID) { %>
|
||||
<div>
|
||||
<% var silence = silences[silenceID] %>
|
||||
@@ -217,24 +214,9 @@
|
||||
<% labelMap[text].hits++ %>
|
||||
<% } %>
|
||||
<% }) %>
|
||||
<% var silencedText = '@silenced: false' %>
|
||||
<% var isSilenced = 'false' %>
|
||||
<% if (alert.silencedBy.length) { %>
|
||||
<% silencedText = '@silenced: true' %>
|
||||
<% isSilenced = 'true' %>
|
||||
<% } %>
|
||||
<% if (labelMap[silencedText] == undefined) { labelMap[silencedText] = {key: '@silenced', value: isSilenced, hits: 0} } %>
|
||||
<% labelMap[silencedText].hits++ %>
|
||||
|
||||
<% var inhibitedText = '@inhibited: false' %>
|
||||
<% var isInhibited = 'false' %>
|
||||
<% if (alert.inhibitedBy.length) { %>
|
||||
<% inhibitedText = '@inhibited: true' %>
|
||||
<% isInhibited = 'true' %>
|
||||
<% } %>
|
||||
<% if (labelMap[inhibitedText] == undefined) { labelMap[inhibitedText] = {key: '@inhibited', value: isInhibited, hits: 0} } %>
|
||||
<% labelMap[inhibitedText].hits++ %>
|
||||
|
||||
<% var statusText = '@status: ' + alert.status %>
|
||||
<% if (labelMap[statusText] == undefined) { labelMap[statusText] = {key: '@status', value: alert.status, hits: 0} } %>
|
||||
<% labelMap[statusText].hits++ %>
|
||||
<% } else { %>
|
||||
<% var cls_body = '' %>
|
||||
<% if (i < group.alerts.length - 1) { cls_body = 'incident-group-separator' } %>
|
||||
|
||||
@@ -130,40 +130,24 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td id="help-silenced">
|
||||
<code>@silenced=(true false)</code>
|
||||
<td id="help-status">
|
||||
<code>@status=(active suppresed unprocessed)</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Match alerts based on silence status.</p>
|
||||
<p>Match alerts based on the status.</p>
|
||||
<table class="table examples">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="label label-info">@silenced=true</span></td>
|
||||
<td>Match only silenced alerts.</td>
|
||||
<td><span class="label label-info">@status=active</span></td>
|
||||
<td>Match only active alerts.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="label label-info">@silenced=false</span></td>
|
||||
<td>Match only unsilenced alerts.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="help-inhibited">
|
||||
<code>@inhibited=(true false)</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Match alerts based on inhibition status.</p>
|
||||
<table class="table examples">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="label label-info">@inhibited=true</span></td>
|
||||
<td>Match only inhibited alerts.</td>
|
||||
<td><span class="label label-info">@status=suppressed</span></td>
|
||||
<td>Match only suppressed alerts.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="label label-info">@inhibited=false</span></td>
|
||||
<td>Match only non-inhibited alerts.</td>
|
||||
<td><span class="label label-info">@status=unprocessed</span></td>
|
||||
<td>Match only unprocessed alerts.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -179,15 +163,15 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="label label-info">@silence_author=me@domain1.com</span></td>
|
||||
<td>Match alerts silenced by <em>me@domain1.com</em>.</td>
|
||||
<td>Match alerts status by <em>me@domain1.com</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="label label-info">@silence_author!=me@domain1.com</span></td>
|
||||
<td>Match alerts not silenced by <em>me@domain1.com</em>.</td>
|
||||
<td>Match alerts not status by <em>me@domain1.com</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="label label-info">@silence_author=~@domain2.com</span></td>
|
||||
<td>Match alerts silenced by username that match regular expression <code>/.*@domain2.com.*/</code>.</td>
|
||||
<td>Match alerts status by username that match regular expression <code>/.*@domain2.com.*/</code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -203,15 +187,15 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="label label-info">@silence_jira=PROJECT-123</span></td>
|
||||
<td>Match silenced alerts where detected JIRA issue id is equal to <em>PROJECT-123</em>.</td>
|
||||
<td>Match status alerts where detected JIRA issue id is equal to <em>PROJECT-123</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="label label-info">@silence_jira!=PROJECT-123</span></td>
|
||||
<td>Match silenced alerts where there was no JIRA issue id detected or it was not equal to <em>PROJECT-123</em>.</td>
|
||||
<td>Match status alerts where there was no JIRA issue id detected or it was not equal to <em>PROJECT-123</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="label label-info">@silence_jira=~PROJECT</span></td>
|
||||
<td>Match silenced alerts where dectected JIRA issue id matches regular expression <code>/.*PROJECT.*/</code>.</td>
|
||||
<td>Match status alerts where dectected JIRA issue id matches regular expression <code>/.*PROJECT.*/</code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,72 +0,0 @@
|
||||
package filters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudflare/unsee/models"
|
||||
)
|
||||
|
||||
type inhibitedFilter struct {
|
||||
alertFilter
|
||||
}
|
||||
|
||||
func (filter *inhibitedFilter) init(name string, matcher *matcherT, rawText string, isValid bool, value string) {
|
||||
filter.Matched = name
|
||||
if matcher != nil {
|
||||
filter.Matcher = *matcher
|
||||
}
|
||||
filter.RawText = rawText
|
||||
filter.IsValid = isValid
|
||||
switch value {
|
||||
case "true":
|
||||
filter.Value = true
|
||||
case "false":
|
||||
filter.Value = false
|
||||
default:
|
||||
filter.IsValid = false
|
||||
}
|
||||
}
|
||||
|
||||
func (filter *inhibitedFilter) Match(alert *models.Alert, matches int) bool {
|
||||
if filter.IsValid {
|
||||
isMatch := filter.Matcher.Compare(alert.IsInhibited(), filter.Value)
|
||||
if isMatch {
|
||||
filter.Hits++
|
||||
}
|
||||
return isMatch
|
||||
}
|
||||
e := fmt.Sprintf("Match() called on invalid filter %#v", filter)
|
||||
panic(e)
|
||||
}
|
||||
|
||||
func newInhibitedFilter() FilterT {
|
||||
f := inhibitedFilter{}
|
||||
return &f
|
||||
}
|
||||
|
||||
func inhibitedAutocomplete(name string, operators []string, alerts []models.Alert) []models.Autocomplete {
|
||||
tokens := []models.Autocomplete{}
|
||||
for _, operator := range operators {
|
||||
switch operator {
|
||||
case equalOperator:
|
||||
tokens = append(tokens, makeAC(
|
||||
fmt.Sprintf("%s%strue", name, operator),
|
||||
[]string{
|
||||
name,
|
||||
strings.TrimPrefix(name, "@"),
|
||||
fmt.Sprintf("%s%s", name, operator),
|
||||
},
|
||||
))
|
||||
tokens = append(tokens, makeAC(
|
||||
fmt.Sprintf("%s%sfalse", name, operator),
|
||||
[]string{
|
||||
name,
|
||||
strings.TrimPrefix(name, "@"),
|
||||
fmt.Sprintf("%s%s", name, operator),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package filters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudflare/unsee/models"
|
||||
)
|
||||
|
||||
type silencedFilter struct {
|
||||
alertFilter
|
||||
}
|
||||
|
||||
func (filter *silencedFilter) init(name string, matcher *matcherT, rawText string, isValid bool, value string) {
|
||||
filter.Matched = name
|
||||
if matcher != nil {
|
||||
filter.Matcher = *matcher
|
||||
}
|
||||
filter.RawText = rawText
|
||||
filter.IsValid = isValid
|
||||
switch value {
|
||||
case "true":
|
||||
filter.Value = true
|
||||
case "false":
|
||||
filter.Value = false
|
||||
default:
|
||||
filter.IsValid = false
|
||||
}
|
||||
}
|
||||
|
||||
func (filter *silencedFilter) Match(alert *models.Alert, matches int) bool {
|
||||
if filter.IsValid {
|
||||
isMatch := filter.Matcher.Compare(alert.IsSilenced(), filter.Value)
|
||||
if isMatch {
|
||||
filter.Hits++
|
||||
}
|
||||
return isMatch
|
||||
}
|
||||
e := fmt.Sprintf("Match() called on invalid filter %#v", filter)
|
||||
panic(e)
|
||||
}
|
||||
|
||||
func newSilencedFilter() FilterT {
|
||||
f := silencedFilter{}
|
||||
return &f
|
||||
}
|
||||
|
||||
func silencedAutocomplete(name string, operators []string, alerts []models.Alert) []models.Autocomplete {
|
||||
tokens := []models.Autocomplete{}
|
||||
for _, operator := range operators {
|
||||
switch operator {
|
||||
case equalOperator:
|
||||
tokens = append(tokens, makeAC(
|
||||
fmt.Sprintf("%s%strue", name, operator),
|
||||
[]string{
|
||||
name,
|
||||
strings.TrimPrefix(name, "@"),
|
||||
fmt.Sprintf("%s%s", name, operator),
|
||||
},
|
||||
))
|
||||
tokens = append(tokens, makeAC(
|
||||
fmt.Sprintf("%s%sfalse", name, operator),
|
||||
[]string{
|
||||
name,
|
||||
strings.TrimPrefix(name, "@"),
|
||||
fmt.Sprintf("%s%s", name, operator),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
59
filters/filter_status.go
Normal file
59
filters/filter_status.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package filters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudflare/unsee/models"
|
||||
)
|
||||
|
||||
type statusFilter struct {
|
||||
alertFilter
|
||||
}
|
||||
|
||||
func (filter *statusFilter) init(name string, matcher *matcherT, rawText string, isValid bool, value string) {
|
||||
filter.Matched = name
|
||||
if matcher != nil {
|
||||
filter.Matcher = *matcher
|
||||
}
|
||||
filter.RawText = rawText
|
||||
filter.IsValid = isValid
|
||||
filter.Value = value
|
||||
if !stringInSlice(models.AlertStateList, value) {
|
||||
filter.IsValid = false
|
||||
}
|
||||
}
|
||||
|
||||
func (filter *statusFilter) Match(alert *models.Alert, matches int) bool {
|
||||
if filter.IsValid {
|
||||
isMatch := filter.Matcher.Compare(alert.Status, filter.Value)
|
||||
if isMatch {
|
||||
filter.Hits++
|
||||
}
|
||||
return isMatch
|
||||
}
|
||||
e := fmt.Sprintf("Match() called on invalid filter %#v", filter)
|
||||
panic(e)
|
||||
}
|
||||
|
||||
func newstatusFilter() FilterT {
|
||||
f := statusFilter{}
|
||||
return &f
|
||||
}
|
||||
|
||||
func statusAutocomplete(name string, operators []string, alerts []models.Alert) []models.Autocomplete {
|
||||
tokens := []models.Autocomplete{}
|
||||
for _, operator := range operators {
|
||||
for _, alert := range alerts {
|
||||
tokens = append(tokens, makeAC(
|
||||
name+operator+alert.Status,
|
||||
[]string{
|
||||
name,
|
||||
strings.TrimPrefix(name, "@"),
|
||||
name + operator,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
@@ -20,100 +20,67 @@ type filterTest struct {
|
||||
|
||||
var tests = []filterTest{
|
||||
filterTest{
|
||||
Expression: "@silenced=true",
|
||||
Expression: "@status=active",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{},
|
||||
IsMatch: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@silenced!=true",
|
||||
Expression: "@status!=active",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{},
|
||||
IsMatch: true,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@silenced=true",
|
||||
Expression: "@status=suppressed",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{Status: "suppressed", SilencedBy: []string{"1"}},
|
||||
IsMatch: true,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@silenced!=true",
|
||||
Expression: "@status!=suppressed",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{Status: "suppressed", SilencedBy: []string{"1"}},
|
||||
IsMatch: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@silenced=xx",
|
||||
Expression: "@status=xx",
|
||||
IsValid: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@silenced=:xx",
|
||||
Expression: "@status=:xx",
|
||||
IsValid: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@silenced==xx",
|
||||
Expression: "@status==xx",
|
||||
IsValid: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@silenced=~true",
|
||||
Expression: "@status=~true",
|
||||
IsValid: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@silenced=~false",
|
||||
Expression: "@status=~false",
|
||||
IsValid: false,
|
||||
},
|
||||
|
||||
filterTest{
|
||||
Expression: "@inhibited=true",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{},
|
||||
IsMatch: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited!=true",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{},
|
||||
IsMatch: true,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited=true",
|
||||
Expression: "@status=suppressed",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{Status: "suppressed", InhibitedBy: []string{"999"}},
|
||||
IsMatch: true,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited=true",
|
||||
Expression: "@status=suppressed",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{Status: "active"},
|
||||
IsMatch: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited!=true",
|
||||
Expression: "@status!=suppressed",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{Status: "suppressed", InhibitedBy: []string{"999"}},
|
||||
IsMatch: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited=xx",
|
||||
IsValid: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited=:xx",
|
||||
IsValid: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited==xx",
|
||||
IsValid: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited=~true",
|
||||
IsValid: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "@inhibited=~false",
|
||||
IsValid: false,
|
||||
},
|
||||
|
||||
filterTest{
|
||||
Expression: "@silence_jira=1",
|
||||
|
||||
@@ -36,16 +36,10 @@ type filterConfig struct {
|
||||
// support
|
||||
var AllFilters = []filterConfig{
|
||||
filterConfig{
|
||||
Label: "@silenced",
|
||||
Label: "@status",
|
||||
SupportedOperators: []string{equalOperator, notEqualOperator},
|
||||
Factory: newSilencedFilter,
|
||||
Autocomplete: silencedAutocomplete,
|
||||
},
|
||||
filterConfig{
|
||||
Label: "@inhibited",
|
||||
SupportedOperators: []string{equalOperator, notEqualOperator},
|
||||
Factory: newInhibitedFilter,
|
||||
Autocomplete: inhibitedAutocomplete,
|
||||
Factory: newstatusFilter,
|
||||
Autocomplete: statusAutocomplete,
|
||||
},
|
||||
filterConfig{
|
||||
Label: "@age",
|
||||
|
||||
6
views.go
6
views.go
@@ -7,7 +7,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -163,17 +162,14 @@ func alerts(c *gin.Context) {
|
||||
io.WriteString(h, string(aj))
|
||||
|
||||
if alert.IsSilenced() {
|
||||
countLabel(counters, "@silenced", "true")
|
||||
for _, silenceID := range alert.SilencedBy {
|
||||
if silence := store.Store.GetSilence(silenceID); silence != nil {
|
||||
silences[silenceID] = *silence
|
||||
}
|
||||
}
|
||||
} else {
|
||||
countLabel(counters, "@silenced", "false")
|
||||
}
|
||||
|
||||
countLabel(counters, "@inhibited", strconv.FormatBool(alert.IsInhibited()))
|
||||
countLabel(counters, "@status", alert.Status)
|
||||
|
||||
if alert.IsActive() {
|
||||
agCopy.ActiveCount++
|
||||
|
||||
@@ -149,7 +149,7 @@ func TestAlerts(t *testing.T) {
|
||||
if ur.Status != "success" {
|
||||
t.Errorf("[%s] Invalid status in response: %s", version, ur.Status)
|
||||
}
|
||||
if len(ur.Counters) != 6 {
|
||||
if len(ur.Counters) != 5 {
|
||||
t.Errorf("[%s] Invalid number of counters in response (%d): %v", version, len(ur.Counters), ur.Counters)
|
||||
}
|
||||
for _, ag := range ur.AlertGroups {
|
||||
@@ -280,16 +280,16 @@ var acTests = []acTestCase{
|
||||
"@age<1h",
|
||||
"@age>10m",
|
||||
"@age>1h",
|
||||
"@inhibited=false",
|
||||
"@inhibited=true",
|
||||
"@limit=10",
|
||||
"@limit=50",
|
||||
"@silence_author!=john@example.com",
|
||||
"@silence_author!~john@example.com",
|
||||
"@silence_author=john@example.com",
|
||||
"@silence_author=~john@example.com",
|
||||
"@silenced=false",
|
||||
"@silenced=true",
|
||||
"@status!=active",
|
||||
"@status!=suppressed",
|
||||
"@status=active",
|
||||
"@status=suppressed",
|
||||
},
|
||||
},
|
||||
acTestCase{
|
||||
|
||||
Reference in New Issue
Block a user