mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-26 05:10:39 +01:00
#### Description - New `GET /api/v3/rules` list API for alert rules: filter query DSL, `states` filter, sort, and offset pagination (design discussion: SigNoz/pulse-pod#324). - Based on #12806, which extracts the shared list filter SQL compiler; this PR adds only the rules key-policy resolver (`sqlrulestore/filterquery_resolver.go`) on top of it. - Rule state lives only in the rule manager's memory, so state filtering, total, sort and pagination run in code after the SQL fetch; total always equals what is pageable. - Sorting is deterministic on ties: equal rows break on name then id, always ascending, so pages never overlap or drop rows between requests. - Response rows carry only list-page fields, deliberately excluding `condition`, `annotations` and `notificationSettings`. The envelope also returns the org's distinct label pairs and the reserved filter keys for suggestions. - Also guards previously unlocked reads of the rules map (`ListRuleStates`, `GetRule`, `TriggeredAlerts`). **Filter keys and operators** | Key | Operators | Notes | |---|---|---| | `name`, `created_by`, `updated_by` | `=`, `!=`, `CONTAINS`, `LIKE`, `ILIKE`, `IN` and negations | string search | | `labels.<key>` | string operators plus `EXISTS`, `NOT EXISTS` | missing label evaluates as empty string; keys are case-sensitive | | `severity` | same as `labels.<key>` | alias for `labels.severity` | | `created_at`, `updated_at` | `=`, `!=`, `<`, `<=`, `>`, `>=`, `BETWEEN`, `NOT BETWEEN` | quoted RFC3339 values | | `alert_type` | `=`, `!=`, `IN`, `NOT IN` | enum: `METRIC_BASED_ALERT`, `TRACES_BASED_ALERT`, `LOGS_BASED_ALERT`, `EXCEPTIONS_BASED_ALERT` | | `rule_type` | `=`, `!=`, `IN`, `NOT IN` | enum: `threshold_rule`, `promql_rule`, `anomaly_rule` | - A bare word is free text: a case-insensitive substring match over name, description and labels. - `state` is not a DSL key. It is the repeated `states=` query param: `firing`, `pending`, `recovering`, `inactive`, `nodata`, `disabled`. - An unknown key or `REGEXP` returns a 400. #### Issues closed by this PR Closes SigNoz/pulse-pod#226 #### Additional Information - A missing label evaluates as the empty string for every value operator, one uniform rule instead of the querier's per-operator split ([`AddDefaultExistsFilter`](https://github.com/SigNoz/signoz/blob/e0da06f76d/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go#L160)); presence is asked with `EXISTS` / `NOT EXISTS`. - Integration tests (`tests/integration/tests/alerts/06_list_rules_v3.py`) cover filters, states, sorting, pagination, totals and the error contract, run against both sqlite and postgres. - Found while testing: the stock `create_notification_channel` fixture teardown silently fails and leaks channels; follow-up fix needed. --------- Co-authored-by: Naman Verma <naman.verma@signoz.io>
130 lines
5.5 KiB
Go
130 lines
5.5 KiB
Go
package sqlstore
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"github.com/SigNoz/signoz/pkg/errors"
|
|
"github.com/uptrace/bun"
|
|
)
|
|
|
|
type SQLStoreTxOptions = sql.TxOptions
|
|
|
|
type SQLStore interface {
|
|
// SQLDB returns the underlying sql.DB.
|
|
SQLDB() *sql.DB
|
|
|
|
// BunDB returns an instance of bun.DB. This is the recommended way to interact with the database.
|
|
BunDB() *bun.DB
|
|
|
|
// Returns the dialect of the database.
|
|
Dialect() SQLDialect
|
|
|
|
Formatter() SQLFormatter
|
|
|
|
// RunInTxCtx runs the given callback in a transaction. It creates and injects a new context with the transaction.
|
|
// If a transaction is present in the context, it will be used.
|
|
RunInTxCtx(ctx context.Context, opts *SQLStoreTxOptions, cb func(ctx context.Context) error) error
|
|
|
|
// BunDBCtx returns an instance of bun.IDB for the given context.
|
|
// If a transaction is present in the context, it will be used. Otherwise, the default will be used.
|
|
BunDBCtx(ctx context.Context) bun.IDB
|
|
|
|
// WrapNotFoundErrf wraps the given error with the given message and returns it.
|
|
WrapNotFoundErrf(err error, code errors.Code, format string, args ...any) error
|
|
|
|
// WrapAlreadyExistsErrf wraps the given error with the given message and returns it.
|
|
WrapAlreadyExistsErrf(err error, code errors.Code, format string, args ...any) error
|
|
}
|
|
|
|
type SQLStoreHook interface {
|
|
bun.QueryHook
|
|
}
|
|
|
|
type SQLDialect interface {
|
|
// Returns the type of the column for the given table and column.
|
|
GetColumnType(context.Context, bun.IDB, string, string) (string, error)
|
|
|
|
// Migrates an integer column to a timestamp column for the given table and column.
|
|
IntToTimestamp(context.Context, bun.IDB, string, string) error
|
|
|
|
// Migrates an integer column to a boolean column for the given table and column.
|
|
IntToBoolean(context.Context, bun.IDB, string, string) error
|
|
|
|
// Adds a not null default to the given column for the given table, column, columnType and defaultValue.
|
|
AddNotNullDefaultToColumn(context.Context, bun.IDB, string, string, string, string) error
|
|
|
|
// Checks if a column exists in a table for the given table and column.
|
|
ColumnExists(context.Context, bun.IDB, string, string) (bool, error)
|
|
|
|
// Adds a column to a table for the given table, column and columnType.
|
|
AddColumn(context.Context, bun.IDB, string, string, string) error
|
|
|
|
// Drops a column from a table for the given table and column.
|
|
DropColumn(context.Context, bun.IDB, string, string) error
|
|
|
|
// Renames a column in a table for the given table, old column name and new column name.
|
|
RenameColumn(context.Context, bun.IDB, string, string, string) (bool, error)
|
|
|
|
// Renames a table and modifies the given model for the given table, old model, new model, references and callback. The old model
|
|
// and new model must inherit bun.BaseModel.
|
|
RenameTableAndModifyModel(context.Context, bun.IDB, interface{}, interface{}, []string, func(context.Context) error) error
|
|
|
|
// Updates the primary key for the given table, old model, new model, reference and callback. The old model and new model
|
|
// must inherit bun.BaseModel.
|
|
UpdatePrimaryKey(context.Context, bun.IDB, interface{}, interface{}, string, func(context.Context) error) error
|
|
|
|
// Adds a primary key to the given table, old model, new model, reference and callback. The old model and new model
|
|
// must inherit bun.BaseModel.
|
|
AddPrimaryKey(context.Context, bun.IDB, interface{}, interface{}, string, func(context.Context) error) error
|
|
|
|
// Drops the column and the associated foreign key constraint for the given table and column.
|
|
DropColumnWithForeignKeyConstraint(context.Context, bun.IDB, interface{}, string) error
|
|
|
|
// Checks if a table exists.
|
|
TableExists(ctx context.Context, bun bun.IDB, table interface{}) (bool, error)
|
|
|
|
// Toggles foreign key constraint for the given database. This makes sense only for sqlite. This cannot take a transaction as an argument and needs to take the db
|
|
// as an argument.
|
|
ToggleForeignKeyConstraint(ctx context.Context, bun *bun.DB, enable bool) error
|
|
}
|
|
|
|
type SQLFormatter interface {
|
|
// JSONExtractString takes a JSON path (e.g., "$.labels.severity")
|
|
JSONExtractString(column, path string) []byte
|
|
|
|
// JSONType used to determine the type of the value extracted from the path
|
|
JSONType(column, path string) []byte
|
|
|
|
// JSONIsArray used to check whether the value is array or not
|
|
JSONIsArray(column, path string) []byte
|
|
|
|
// JSONArrayElements returns query as well as columns alias to be used for select and where clause
|
|
JSONArrayElements(column, path, alias string) ([]byte, []byte)
|
|
|
|
// JSONArrayOfStrings returns query as well as columns alias to be used for select and where clause
|
|
JSONArrayOfStrings(column, path, alias string) ([]byte, []byte)
|
|
|
|
// JSONArrayAgg aggregates values into a JSON array
|
|
JSONArrayAgg(expression string) []byte
|
|
|
|
// JSONArrayLiteral creates a literal JSON array from the given string values
|
|
JSONArrayLiteral(values ...string) []byte
|
|
|
|
// JSONKeys return extracted key from json as well as alias to be used for select and where clause
|
|
JSONKeys(column, path, alias string) ([]byte, []byte)
|
|
|
|
// JSONExtractMapValue extracts one key's value from a JSON object field; dots in the key are not path nesting.
|
|
JSONExtractMapValue(column, mapField, key string) []byte
|
|
|
|
// TextToJsonColumn converts a text column to JSON type
|
|
TextToJsonColumn(column string) []byte
|
|
|
|
// LowerExpression wraps any SQL expression with lower() function for case-insensitive operations
|
|
LowerExpression(expression string) []byte
|
|
|
|
// EscapeLikePattern escapes LIKE wildcards (`%`, `_`, and the escape char `\`)
|
|
// in a value so it matches literally. Pair the pattern with `ESCAPE '\'`.
|
|
EscapeLikePattern(value string) string
|
|
}
|