Compare commits

...

1 Commits

Author SHA1 Message Date
Pandey
61ae49d4ab refactor(ruler): introduce v2 Rule read type and validate uuid on delete (#10997)
Some checks are pending
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
build-staging / staging (push) Blocked by required conditions
Release Drafter / update_release_draft (push) Waiting to run
* refactor(ruler): add Rule v2 read type and rename storage Rule to StorableRule

* refactor(ruler): map GettableRule to Rule before responding on v2 routes

* docs(openapi): regenerate spec with RuletypesRule on v2 rules routes

* docs(frontend): regenerate API clients with RuletypesRuleDTO

* refactor(ruler): validate uuid-v7 on delete rule handler
2026-04-18 12:39:50 +00:00
9 changed files with 245 additions and 187 deletions

View File

@@ -3755,71 +3755,6 @@ components:
spec:
$ref: '#/components/schemas/RuletypesRollingWindow'
type: object
RuletypesGettableRule:
properties:
alert:
type: string
alertType:
$ref: '#/components/schemas/RuletypesAlertType'
annotations:
additionalProperties:
type: string
type: object
condition:
$ref: '#/components/schemas/RuletypesRuleCondition'
createAt:
format: date-time
type: string
createBy:
nullable: true
type: string
description:
type: string
disabled:
type: boolean
evalWindow:
type: string
evaluation:
$ref: '#/components/schemas/RuletypesEvaluationEnvelope'
frequency:
type: string
id:
type: string
labels:
additionalProperties:
type: string
type: object
notificationSettings:
$ref: '#/components/schemas/RuletypesNotificationSettings'
preferredChannels:
items:
type: string
type: array
ruleType:
$ref: '#/components/schemas/RuletypesRuleType'
schemaVersion:
type: string
source:
type: string
state:
$ref: '#/components/schemas/RuletypesAlertState'
updateAt:
format: date-time
type: string
updateBy:
nullable: true
type: string
version:
type: string
required:
- id
- state
- alert
- ruleType
- condition
- createAt
- updateAt
type: object
RuletypesGettableTestRule:
properties:
alertCount:
@@ -4029,6 +3964,67 @@ components:
- evalWindow
- frequency
type: object
RuletypesRule:
properties:
alert:
type: string
alertType:
$ref: '#/components/schemas/RuletypesAlertType'
annotations:
additionalProperties:
type: string
type: object
condition:
$ref: '#/components/schemas/RuletypesRuleCondition'
createdAt:
format: date-time
type: string
createdBy:
type: string
description:
type: string
disabled:
type: boolean
evalWindow:
type: string
evaluation:
$ref: '#/components/schemas/RuletypesEvaluationEnvelope'
frequency:
type: string
id:
type: string
labels:
additionalProperties:
type: string
type: object
notificationSettings:
$ref: '#/components/schemas/RuletypesNotificationSettings'
preferredChannels:
items:
type: string
type: array
ruleType:
$ref: '#/components/schemas/RuletypesRuleType'
schemaVersion:
type: string
source:
type: string
state:
$ref: '#/components/schemas/RuletypesAlertState'
updatedAt:
format: date-time
type: string
updatedBy:
type: string
version:
type: string
required:
- id
- state
- alert
- ruleType
- condition
type: object
RuletypesRuleCondition:
properties:
absentFor:
@@ -10776,7 +10772,7 @@ paths:
properties:
data:
items:
$ref: '#/components/schemas/RuletypesGettableRule'
$ref: '#/components/schemas/RuletypesRule'
type: array
status:
type: string
@@ -10827,7 +10823,7 @@ paths:
schema:
properties:
data:
$ref: '#/components/schemas/RuletypesGettableRule'
$ref: '#/components/schemas/RuletypesRule'
status:
type: string
required:
@@ -10930,7 +10926,7 @@ paths:
schema:
properties:
data:
$ref: '#/components/schemas/RuletypesGettableRule'
$ref: '#/components/schemas/RuletypesRule'
status:
type: string
required:
@@ -10992,7 +10988,7 @@ paths:
schema:
properties:
data:
$ref: '#/components/schemas/RuletypesGettableRule'
$ref: '#/components/schemas/RuletypesRule'
status:
type: string
required:

View File

@@ -4659,87 +4659,6 @@ export interface RuletypesEvaluationRollingDTO {
spec?: RuletypesRollingWindowDTO;
}
export type RuletypesGettableRuleDTOAnnotations = { [key: string]: string };
export type RuletypesGettableRuleDTOLabels = { [key: string]: string };
export interface RuletypesGettableRuleDTO {
/**
* @type string
*/
alert: string;
alertType?: RuletypesAlertTypeDTO;
/**
* @type object
*/
annotations?: RuletypesGettableRuleDTOAnnotations;
condition: RuletypesRuleConditionDTO;
/**
* @type string
* @format date-time
*/
createAt: Date;
/**
* @type string
* @nullable true
*/
createBy?: string | null;
/**
* @type string
*/
description?: string;
/**
* @type boolean
*/
disabled?: boolean;
/**
* @type string
*/
evalWindow?: string;
evaluation?: RuletypesEvaluationEnvelopeDTO;
/**
* @type string
*/
frequency?: string;
/**
* @type string
*/
id: string;
/**
* @type object
*/
labels?: RuletypesGettableRuleDTOLabels;
notificationSettings?: RuletypesNotificationSettingsDTO;
/**
* @type array
*/
preferredChannels?: string[];
ruleType: RuletypesRuleTypeDTO;
/**
* @type string
*/
schemaVersion?: string;
/**
* @type string
*/
source?: string;
state: RuletypesAlertStateDTO;
/**
* @type string
* @format date-time
*/
updateAt: Date;
/**
* @type string
* @nullable true
*/
updateBy?: string | null;
/**
* @type string
*/
version?: string;
}
export interface RuletypesGettableTestRuleDTO {
/**
* @type integer
@@ -4971,6 +4890,85 @@ export interface RuletypesRollingWindowDTO {
frequency: string;
}
export type RuletypesRuleDTOAnnotations = { [key: string]: string };
export type RuletypesRuleDTOLabels = { [key: string]: string };
export interface RuletypesRuleDTO {
/**
* @type string
*/
alert: string;
alertType?: RuletypesAlertTypeDTO;
/**
* @type object
*/
annotations?: RuletypesRuleDTOAnnotations;
condition: RuletypesRuleConditionDTO;
/**
* @type string
* @format date-time
*/
createdAt?: Date;
/**
* @type string
*/
createdBy?: string;
/**
* @type string
*/
description?: string;
/**
* @type boolean
*/
disabled?: boolean;
/**
* @type string
*/
evalWindow?: string;
evaluation?: RuletypesEvaluationEnvelopeDTO;
/**
* @type string
*/
frequency?: string;
/**
* @type string
*/
id: string;
/**
* @type object
*/
labels?: RuletypesRuleDTOLabels;
notificationSettings?: RuletypesNotificationSettingsDTO;
/**
* @type array
*/
preferredChannels?: string[];
ruleType: RuletypesRuleTypeDTO;
/**
* @type string
*/
schemaVersion?: string;
/**
* @type string
*/
source?: string;
state: RuletypesAlertStateDTO;
/**
* @type string
* @format date-time
*/
updatedAt?: Date;
/**
* @type string
*/
updatedBy?: string;
/**
* @type string
*/
version?: string;
}
export interface RuletypesRuleConditionDTO {
/**
* @type integer
@@ -6831,7 +6829,7 @@ export type ListRules200 = {
/**
* @type array
*/
data: RuletypesGettableRuleDTO[];
data: RuletypesRuleDTO[];
/**
* @type string
*/
@@ -6839,7 +6837,7 @@ export type ListRules200 = {
};
export type CreateRule201 = {
data: RuletypesGettableRuleDTO;
data: RuletypesRuleDTO;
/**
* @type string
*/
@@ -6853,7 +6851,7 @@ export type GetRuleByIDPathParameters = {
id: string;
};
export type GetRuleByID200 = {
data: RuletypesGettableRuleDTO;
data: RuletypesRuleDTO;
/**
* @type string
*/
@@ -6864,7 +6862,7 @@ export type PatchRuleByIDPathParameters = {
id: string;
};
export type PatchRuleByID200 = {
data: RuletypesGettableRuleDTO;
data: RuletypesRuleDTO;
/**
* @type string
*/

View File

@@ -15,7 +15,7 @@ func (provider *provider) addRulerRoutes(router *mux.Router) error {
Tags: []string{"rules"},
Summary: "List alert rules",
Description: "This endpoint lists all alert rules with their current evaluation state",
Response: make([]*ruletypes.GettableRule, 0),
Response: make([]*ruletypes.Rule, 0),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
@@ -28,7 +28,7 @@ func (provider *provider) addRulerRoutes(router *mux.Router) error {
Tags: []string{"rules"},
Summary: "Get alert rule by ID",
Description: "This endpoint returns an alert rule by ID",
Response: new(ruletypes.GettableRule),
Response: new(ruletypes.Rule),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusNotFound},
@@ -44,7 +44,7 @@ func (provider *provider) addRulerRoutes(router *mux.Router) error {
Description: "This endpoint creates a new alert rule",
Request: new(ruletypes.PostableRule),
RequestContentType: "application/json",
Response: new(ruletypes.GettableRule),
Response: new(ruletypes.Rule),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusCreated,
ErrorStatusCodes: []int{http.StatusBadRequest},
@@ -86,7 +86,7 @@ func (provider *provider) addRulerRoutes(router *mux.Router) error {
Description: "This endpoint applies a partial update to an alert rule by ID",
Request: new(ruletypes.PostableRule),
RequestContentType: "application/json",
Response: new(ruletypes.GettableRule),
Response: new(ruletypes.Rule),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},

View File

@@ -577,7 +577,7 @@ func (m *Manager) CreateRule(ctx context.Context, ruleStr string) (*ruletypes.Ge
return nil, err
}
now := time.Now()
storedRule := &ruletypes.Rule{
storedRule := &ruletypes.StorableRule{
Identifiable: types.Identifiable{
ID: valuer.GenerateUUID(),
},

View File

@@ -40,12 +40,12 @@ func (m *MockSQLRuleStore) Mock() sqlmock.Sqlmock {
}
// CreateRule implements ruletypes.RuleStore - delegates to underlying ruleStore to trigger SQL.
func (m *MockSQLRuleStore) CreateRule(ctx context.Context, rule *ruletypes.Rule, fn func(context.Context, valuer.UUID) error) (valuer.UUID, error) {
func (m *MockSQLRuleStore) CreateRule(ctx context.Context, rule *ruletypes.StorableRule, fn func(context.Context, valuer.UUID) error) (valuer.UUID, error) {
return m.ruleStore.CreateRule(ctx, rule, fn)
}
// EditRule implements ruletypes.RuleStore - delegates to underlying ruleStore to trigger SQL.
func (m *MockSQLRuleStore) EditRule(ctx context.Context, rule *ruletypes.Rule, fn func(context.Context) error) error {
func (m *MockSQLRuleStore) EditRule(ctx context.Context, rule *ruletypes.StorableRule, fn func(context.Context) error) error {
return m.ruleStore.EditRule(ctx, rule, fn)
}
@@ -55,12 +55,12 @@ func (m *MockSQLRuleStore) DeleteRule(ctx context.Context, id valuer.UUID, fn fu
}
// GetStoredRule implements ruletypes.RuleStore - delegates to underlying ruleStore to trigger SQL.
func (m *MockSQLRuleStore) GetStoredRule(ctx context.Context, id valuer.UUID) (*ruletypes.Rule, error) {
func (m *MockSQLRuleStore) GetStoredRule(ctx context.Context, id valuer.UUID) (*ruletypes.StorableRule, error) {
return m.ruleStore.GetStoredRule(ctx, id)
}
// GetStoredRules implements ruletypes.RuleStore - delegates to underlying ruleStore to trigger SQL.
func (m *MockSQLRuleStore) GetStoredRules(ctx context.Context, orgID string) ([]*ruletypes.Rule, error) {
func (m *MockSQLRuleStore) GetStoredRules(ctx context.Context, orgID string) ([]*ruletypes.StorableRule, error) {
return m.ruleStore.GetStoredRules(ctx, orgID)
}
@@ -70,7 +70,7 @@ func (m *MockSQLRuleStore) GetStoredRulesByMetricName(ctx context.Context, orgID
}
// ExpectCreateRule sets up SQL expectations for CreateRule operation.
func (m *MockSQLRuleStore) ExpectCreateRule(rule *ruletypes.Rule) {
func (m *MockSQLRuleStore) ExpectCreateRule(rule *ruletypes.StorableRule) {
rows := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "created_by", "updated_by", "deleted", "data", "org_id"}).
AddRow(rule.ID, rule.CreatedAt, rule.UpdatedAt, rule.CreatedBy, rule.UpdatedBy, rule.Deleted, rule.Data, rule.OrgID)
expectedPattern := `INSERT INTO "rule" \(.+\) VALUES \(.+` +
@@ -81,7 +81,7 @@ func (m *MockSQLRuleStore) ExpectCreateRule(rule *ruletypes.Rule) {
}
// ExpectEditRule sets up SQL expectations for EditRule operation.
func (m *MockSQLRuleStore) ExpectEditRule(rule *ruletypes.Rule) {
func (m *MockSQLRuleStore) ExpectEditRule(rule *ruletypes.StorableRule) {
expectedPattern := `UPDATE "rule".+` + rule.UpdatedBy + `.+` + rule.OrgID + `.+WHERE \(id = '` + rule.ID.StringValue() + `'\)`
m.mock.ExpectExec(expectedPattern).
WillReturnResult(sqlmock.NewResult(1, 1))
@@ -95,7 +95,7 @@ func (m *MockSQLRuleStore) ExpectDeleteRule(ruleID valuer.UUID) {
}
// ExpectGetStoredRule sets up SQL expectations for GetStoredRule operation.
func (m *MockSQLRuleStore) ExpectGetStoredRule(ruleID valuer.UUID, rule *ruletypes.Rule) {
func (m *MockSQLRuleStore) ExpectGetStoredRule(ruleID valuer.UUID, rule *ruletypes.StorableRule) {
rows := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "created_by", "updated_by", "deleted", "data", "org_id"}).
AddRow(rule.ID, rule.CreatedAt, rule.UpdatedAt, rule.CreatedBy, rule.UpdatedBy, rule.Deleted, rule.Data, rule.OrgID)
expectedPattern := `SELECT (.+) FROM "rule".+WHERE \(id = '` + ruleID.StringValue() + `'\)`
@@ -104,7 +104,7 @@ func (m *MockSQLRuleStore) ExpectGetStoredRule(ruleID valuer.UUID, rule *ruletyp
}
// ExpectGetStoredRules sets up SQL expectations for GetStoredRules operation.
func (m *MockSQLRuleStore) ExpectGetStoredRules(orgID string, rules []*ruletypes.Rule) {
func (m *MockSQLRuleStore) ExpectGetStoredRules(orgID string, rules []*ruletypes.StorableRule) {
rows := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "created_by", "updated_by", "deleted", "data", "org_id"})
for _, rule := range rules {
rows.AddRow(rule.ID, rule.CreatedAt, rule.UpdatedAt, rule.CreatedBy, rule.UpdatedBy, rule.Deleted, rule.Data, rule.OrgID)
@@ -115,7 +115,7 @@ func (m *MockSQLRuleStore) ExpectGetStoredRules(orgID string, rules []*ruletypes
}
// ExpectGetStoredRulesByMetricName sets up SQL expectations for GetStoredRulesByMetricName operation.
func (m *MockSQLRuleStore) ExpectGetStoredRulesByMetricName(orgID string, metricName string, rules []*ruletypes.Rule) {
func (m *MockSQLRuleStore) ExpectGetStoredRulesByMetricName(orgID string, metricName string, rules []*ruletypes.StorableRule) {
rows := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "created_by", "updated_by", "deleted", "data", "org_id"})
for _, rule := range rules {
rows.AddRow(rule.ID, rule.CreatedAt, rule.UpdatedAt, rule.CreatedBy, rule.UpdatedBy, rule.Deleted, rule.Data, rule.OrgID)

View File

@@ -30,7 +30,7 @@ func NewRuleStore(store sqlstore.SQLStore, queryParser queryparser.QueryParser,
}
}
func (r *rule) CreateRule(ctx context.Context, storedRule *ruletypes.Rule, cb func(context.Context, valuer.UUID) error) (valuer.UUID, error) {
func (r *rule) CreateRule(ctx context.Context, storedRule *ruletypes.StorableRule, cb func(context.Context, valuer.UUID) error) (valuer.UUID, error) {
err := r.sqlstore.RunInTxCtx(ctx, nil, func(ctx context.Context) error {
_, err := r.sqlstore.
BunDBCtx(ctx).
@@ -51,7 +51,7 @@ func (r *rule) CreateRule(ctx context.Context, storedRule *ruletypes.Rule, cb fu
return storedRule.ID, nil
}
func (r *rule) EditRule(ctx context.Context, storedRule *ruletypes.Rule, cb func(context.Context) error) error {
func (r *rule) EditRule(ctx context.Context, storedRule *ruletypes.StorableRule, cb func(context.Context) error) error {
return r.sqlstore.RunInTxCtx(ctx, nil, func(ctx context.Context) error {
_, err := r.sqlstore.
BunDBCtx(ctx).
@@ -72,7 +72,7 @@ func (r *rule) DeleteRule(ctx context.Context, id valuer.UUID, cb func(context.C
_, err := r.sqlstore.
BunDBCtx(ctx).
NewDelete().
Model(new(ruletypes.Rule)).
Model(new(ruletypes.StorableRule)).
Where("id = ?", id.StringValue()).
Exec(ctx)
if err != nil {
@@ -87,8 +87,8 @@ func (r *rule) DeleteRule(ctx context.Context, id valuer.UUID, cb func(context.C
return nil
}
func (r *rule) GetStoredRules(ctx context.Context, orgID string) ([]*ruletypes.Rule, error) {
rules := make([]*ruletypes.Rule, 0)
func (r *rule) GetStoredRules(ctx context.Context, orgID string) ([]*ruletypes.StorableRule, error) {
rules := make([]*ruletypes.StorableRule, 0)
err := r.sqlstore.
BunDB().
NewSelect().
@@ -102,8 +102,8 @@ func (r *rule) GetStoredRules(ctx context.Context, orgID string) ([]*ruletypes.R
return rules, nil
}
func (r *rule) GetStoredRule(ctx context.Context, id valuer.UUID) (*ruletypes.Rule, error) {
rule := new(ruletypes.Rule)
func (r *rule) GetStoredRule(ctx context.Context, id valuer.UUID) (*ruletypes.StorableRule, error) {
rule := new(ruletypes.StorableRule)
err := r.sqlstore.
BunDB().
NewSelect().

View File

@@ -34,7 +34,12 @@ func (handler *handler) ListRules(rw http.ResponseWriter, req *http.Request) {
return
}
render.Success(rw, http.StatusOK, rules.Rules)
view := make([]*ruletypes.Rule, 0, len(rules.Rules))
for _, rule := range rules.Rules {
view = append(view, ruletypes.NewRule(rule))
}
render.Success(rw, http.StatusOK, view)
}
func (handler *handler) GetRuleByID(rw http.ResponseWriter, req *http.Request) {
@@ -53,7 +58,7 @@ func (handler *handler) GetRuleByID(rw http.ResponseWriter, req *http.Request) {
return
}
render.Success(rw, http.StatusOK, rule)
render.Success(rw, http.StatusOK, ruletypes.NewRule(rule))
}
func (handler *handler) CreateRule(rw http.ResponseWriter, req *http.Request) {
@@ -73,7 +78,7 @@ func (handler *handler) CreateRule(rw http.ResponseWriter, req *http.Request) {
return
}
render.Success(rw, http.StatusCreated, rule)
render.Success(rw, http.StatusCreated, ruletypes.NewRule(rule))
}
func (handler *handler) UpdateRuleByID(rw http.ResponseWriter, req *http.Request) {
@@ -106,9 +111,13 @@ func (handler *handler) DeleteRuleByID(rw http.ResponseWriter, req *http.Request
ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second)
defer cancel()
idStr := mux.Vars(req)["id"]
id, err := valuer.NewUUID(mux.Vars(req)["id"])
if err != nil {
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is not a valid uuid-v7"))
return
}
err := handler.ruler.DeleteRule(ctx, idStr)
err = handler.ruler.DeleteRule(ctx, id.StringValue())
if err != nil {
render.Error(rw, err)
return
@@ -140,7 +149,7 @@ func (handler *handler) PatchRuleByID(rw http.ResponseWriter, req *http.Request)
return
}
render.Success(rw, http.StatusOK, rule)
render.Success(rw, http.StatusOK, ruletypes.NewRule(rule))
}
func (handler *handler) TestRule(rw http.ResponseWriter, req *http.Request) {

View File

@@ -11,6 +11,7 @@ import (
"github.com/prometheus/alertmanager/config"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/alertmanagertypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -640,3 +641,57 @@ func (g *GettableRule) MarshalJSON() ([]byte, error) {
return json.Marshal(aux)
}
}
// Rule is the v2 API read model for an alerting rule. It aligns audit fields
// with the canonical types.TimeAuditable / types.UserAuditable shape used by
// PlannedMaintenance and other entities. v1 handlers keep serializing
// GettableRule directly for back-compat with existing SDK / Terraform clients.
type Rule struct {
Id string `json:"id" required:"true"`
State AlertState `json:"state" required:"true"`
PostableRule
types.TimeAuditable
types.UserAuditable
}
func NewRule(g *GettableRule) *Rule {
r := &Rule{
Id: g.Id,
State: g.State,
PostableRule: g.PostableRule,
}
r.CreatedAt = g.CreatedAt
r.UpdatedAt = g.UpdatedAt
if g.CreatedBy != nil {
r.CreatedBy = *g.CreatedBy
}
if g.UpdatedBy != nil {
r.UpdatedBy = *g.UpdatedBy
}
return r
}
func (r *Rule) MarshalJSON() ([]byte, error) {
type Alias Rule
switch r.SchemaVersion {
case DefaultSchemaVersion:
copyStruct := *r
aux := Alias(copyStruct)
if aux.RuleCondition != nil {
aux.RuleCondition.Thresholds = nil
}
aux.Evaluation = nil
aux.SchemaVersion = ""
aux.NotificationSettings = nil
return json.Marshal(aux)
case SchemaVersionV2Alpha1:
copyStruct := *r
aux := Alias(copyStruct)
return json.Marshal(aux)
default:
copyStruct := *r
aux := Alias(copyStruct)
return json.Marshal(aux)
}
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/uptrace/bun"
)
type Rule struct {
type StorableRule struct {
bun.BaseModel `bun:"table:rule"`
types.Identifiable
types.TimeAuditable
@@ -20,7 +20,7 @@ type Rule struct {
OrgID string `bun:"org_id,type:text"`
}
func NewStatsFromRules(rules []*Rule) map[string]any {
func NewStatsFromRules(rules []*StorableRule) map[string]any {
stats := make(map[string]any)
for _, rule := range rules {
gettableRule := &GettableRule{}
@@ -54,10 +54,10 @@ type RuleAlert struct {
}
type RuleStore interface {
CreateRule(context.Context, *Rule, func(context.Context, valuer.UUID) error) (valuer.UUID, error)
EditRule(context.Context, *Rule, func(context.Context) error) error
CreateRule(context.Context, *StorableRule, func(context.Context, valuer.UUID) error) (valuer.UUID, error)
EditRule(context.Context, *StorableRule, func(context.Context) error) error
DeleteRule(context.Context, valuer.UUID, func(context.Context) error) error
GetStoredRules(context.Context, string) ([]*Rule, error)
GetStoredRule(context.Context, valuer.UUID) (*Rule, error)
GetStoredRules(context.Context, string) ([]*StorableRule, error)
GetStoredRule(context.Context, valuer.UUID) (*StorableRule, error)
GetStoredRulesByMetricName(context.Context, string, string) ([]RuleAlert, error)
}