diff --git a/pkg/services/ngalert/api/api_provisioning.go b/pkg/services/ngalert/api/api_provisioning.go index d1a6a1f1d59..2569d256b9f 100644 --- a/pkg/services/ngalert/api/api_provisioning.go +++ b/pkg/services/ngalert/api/api_provisioning.go @@ -54,6 +54,7 @@ type AlertRuleService interface { CreateAlertRule(ctx context.Context, rule alerting_models.AlertRule, provenance alerting_models.Provenance) (alerting_models.AlertRule, error) UpdateAlertRule(ctx context.Context, rule alerting_models.AlertRule, provenance alerting_models.Provenance) (alerting_models.AlertRule, error) DeleteAlertRule(ctx context.Context, orgID int64, ruleUID string, provenance alerting_models.Provenance) error + GetRuleGroup(ctx context.Context, orgID int64, folder, group string) (definitions.AlertRuleGroup, error) UpdateRuleGroup(ctx context.Context, orgID int64, folderUID, rulegroup string, interval int64) error } @@ -284,7 +285,18 @@ func (srv *ProvisioningSrv) RouteDeleteAlertRule(c *models.ReqContext, UID strin return response.JSON(http.StatusNoContent, "") } -func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *models.ReqContext, ag definitions.AlertRuleGroup, folderUID string, group string) response.Response { +func (srv *ProvisioningSrv) RouteGetAlertRuleGroup(c *models.ReqContext, folder string, group string) response.Response { + g, err := srv.alertRules.GetRuleGroup(c.Req.Context(), c.OrgId, folder, group) + if err != nil { + if errors.Is(err, store.ErrAlertRuleGroupNotFound) { + return ErrResp(http.StatusNotFound, err, "") + } + return ErrResp(http.StatusInternalServerError, err, "") + } + return response.JSON(http.StatusOK, g) +} + +func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *models.ReqContext, ag definitions.AlertRuleGroupMetadata, folderUID string, group string) response.Response { err := srv.alertRules.UpdateRuleGroup(c.Req.Context(), c.OrgId, folderUID, group, ag.Interval) if err != nil { if errors.Is(err, store.ErrOptimisticLock) { diff --git a/pkg/services/ngalert/api/api_provisioning_test.go b/pkg/services/ngalert/api/api_provisioning_test.go index 118dd0edb27..88574902f5f 100644 --- a/pkg/services/ngalert/api/api_provisioning_test.go +++ b/pkg/services/ngalert/api/api_provisioning_test.go @@ -239,6 +239,28 @@ func TestProvisioningApi(t *testing.T) { require.Equal(t, 404, response.Status()) }) }) + + t.Run("alert rule groups", func(t *testing.T) { + t.Run("are present, GET returns 200", func(t *testing.T) { + sut := createProvisioningSrvSut(t) + rc := createTestRequestCtx() + insertRule(t, sut, createTestAlertRule("rule", 1)) + + response := sut.RouteGetAlertRuleGroup(&rc, "folder-uid", "my-cool-group") + + require.Equal(t, 200, response.Status()) + }) + + t.Run("are missing, GET returns 404", func(t *testing.T) { + sut := createProvisioningSrvSut(t) + rc := createTestRequestCtx() + insertRule(t, sut, createTestAlertRule("rule", 1)) + + response := sut.RouteGetAlertRuleGroup(&rc, "folder-uid", "does not exist") + + require.Equal(t, 404, response.Status()) + }) + }) } func createProvisioningSrvSut(t *testing.T) ProvisioningSrv { @@ -382,6 +404,7 @@ func createTestAlertRule(title string, orgID int64) definitions.AlertRule { }, }, RuleGroup: "my-cool-group", + FolderUID: "folder-uid", For: time.Second * 60, NoDataState: models.OK, ExecErrState: models.OkErrState, diff --git a/pkg/services/ngalert/api/authorization.go b/pkg/services/ngalert/api/authorization.go index a2ebca1fb1f..49f2ad16cd2 100644 --- a/pkg/services/ngalert/api/authorization.go +++ b/pkg/services/ngalert/api/authorization.go @@ -185,7 +185,8 @@ func (api *API) authorize(method, path string) web.Handler { http.MethodGet + "/api/v1/provisioning/templates/{name}", http.MethodGet + "/api/v1/provisioning/mute-timings", http.MethodGet + "/api/v1/provisioning/mute-timings/{name}", - http.MethodGet + "/api/v1/provisioning/alert-rules/{UID}": + http.MethodGet + "/api/v1/provisioning/alert-rules/{UID}", + http.MethodGet + "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}": fallback = middleware.ReqOrgAdmin eval = ac.EvalPermission(ac.ActionAlertingProvisioningRead) // organization scope diff --git a/pkg/services/ngalert/api/forked_provisioning.go b/pkg/services/ngalert/api/forked_provisioning.go index 6446ef47bb5..3339ab10d1f 100644 --- a/pkg/services/ngalert/api/forked_provisioning.go +++ b/pkg/services/ngalert/api/forked_provisioning.go @@ -95,6 +95,10 @@ func (f *ForkedProvisioningApi) forkRouteDeleteAlertRule(ctx *models.ReqContext, return f.svc.RouteDeleteAlertRule(ctx, UID) } -func (f *ForkedProvisioningApi) forkRoutePutAlertRuleGroup(ctx *models.ReqContext, ag apimodels.AlertRuleGroup, folder, group string) response.Response { +func (f *ForkedProvisioningApi) forkRouteGetAlertRuleGroup(ctx *models.ReqContext, folder, group string) response.Response { + return f.svc.RouteGetAlertRuleGroup(ctx, folder, group) +} + +func (f *ForkedProvisioningApi) forkRoutePutAlertRuleGroup(ctx *models.ReqContext, ag apimodels.AlertRuleGroupMetadata, folder, group string) response.Response { return f.svc.RoutePutAlertRuleGroup(ctx, ag, folder, group) } diff --git a/pkg/services/ngalert/api/generated_base_api_provisioning.go b/pkg/services/ngalert/api/generated_base_api_provisioning.go index b449a168658..8fb66c89cc1 100644 --- a/pkg/services/ngalert/api/generated_base_api_provisioning.go +++ b/pkg/services/ngalert/api/generated_base_api_provisioning.go @@ -24,6 +24,7 @@ type ProvisioningApiForkingService interface { RouteDeleteMuteTiming(*models.ReqContext) response.Response RouteDeleteTemplate(*models.ReqContext) response.Response RouteGetAlertRule(*models.ReqContext) response.Response + RouteGetAlertRuleGroup(*models.ReqContext) response.Response RouteGetContactpoints(*models.ReqContext) response.Response RouteGetMuteTiming(*models.ReqContext) response.Response RouteGetMuteTimings(*models.ReqContext) response.Response @@ -61,6 +62,11 @@ func (f *ForkedProvisioningApi) RouteGetAlertRule(ctx *models.ReqContext) respon uIDParam := web.Params(ctx.Req)[":UID"] return f.forkRouteGetAlertRule(ctx, uIDParam) } +func (f *ForkedProvisioningApi) RouteGetAlertRuleGroup(ctx *models.ReqContext) response.Response { + folderUIDParam := web.Params(ctx.Req)[":FolderUID"] + groupParam := web.Params(ctx.Req)[":Group"] + return f.forkRouteGetAlertRuleGroup(ctx, folderUIDParam, groupParam) +} func (f *ForkedProvisioningApi) RouteGetContactpoints(ctx *models.ReqContext) response.Response { return f.forkRouteGetContactpoints(ctx) } @@ -113,7 +119,7 @@ func (f *ForkedProvisioningApi) RoutePutAlertRule(ctx *models.ReqContext) respon func (f *ForkedProvisioningApi) RoutePutAlertRuleGroup(ctx *models.ReqContext) response.Response { folderUIDParam := web.Params(ctx.Req)[":FolderUID"] groupParam := web.Params(ctx.Req)[":Group"] - conf := apimodels.AlertRuleGroup{} + conf := apimodels.AlertRuleGroupMetadata{} if err := web.Bind(ctx.Req, &conf); err != nil { return response.Error(http.StatusBadRequest, "bad request data", err) } @@ -203,6 +209,16 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApiForkingServi m, ), ) + group.Get( + toMacaronPath("/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}"), + api.authorize(http.MethodGet, "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}"), + metrics.Instrument( + http.MethodGet, + "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}", + srv.RouteGetAlertRuleGroup, + m, + ), + ) group.Get( toMacaronPath("/api/v1/provisioning/contact-points"), api.authorize(http.MethodGet, "/api/v1/provisioning/contact-points"), diff --git a/pkg/services/ngalert/api/tooling/api.json b/pkg/services/ngalert/api/tooling/api.json index e976a0da3fd..60645a449f3 100644 --- a/pkg/services/ngalert/api/tooling/api.json +++ b/pkg/services/ngalert/api/tooling/api.json @@ -308,7 +308,7 @@ ], "type": "object" }, - "AlertRuleGroup": { + "AlertRuleGroupMetadata": { "properties": { "interval": { "format": "int64", @@ -470,30 +470,6 @@ }, "type": "array" }, - "DashboardAclUpdateItem": { - "properties": { - "permission": { - "$ref": "#/definitions/PermissionType" - }, - "role": { - "enum": [ - "Viewer", - "Editor", - "Admin" - ], - "type": "string" - }, - "teamId": { - "format": "int64", - "type": "integer" - }, - "userId": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, "DateTime": { "description": "DateTime is a time but it serializes to ISO8601 format with millis\nIt knows how to read 3 different variations of a RFC3339 date time.\nMost APIs we encounter want either millisecond or second precision times.\nThis just tries to make it worry-free.", "format": "date-time", @@ -1336,48 +1312,6 @@ }, "type": "array" }, - "MetricRequest": { - "properties": { - "debug": { - "type": "boolean" - }, - "from": { - "description": "From Start time in epoch timestamps in milliseconds or relative using Grafana time units.", - "example": "now-1h", - "type": "string" - }, - "queries": { - "description": "queries.refId – Specifies an identifier of the query. Is optional and default to “A”.\nqueries.datasourceId – Specifies the data source to be queried. Each query in the request must have an unique datasourceId.\nqueries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100.\nqueries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000.", - "example": [ - { - "datasource": { - "uid": "PD8C576611E62080A" - }, - "format": "table", - "intervalMs": 86400000, - "maxDataPoints": 1092, - "rawSql": "SELECT 1 as valueOne, 2 as valueTwo", - "refId": "A" - } - ], - "items": { - "$ref": "#/definitions/Json" - }, - "type": "array" - }, - "to": { - "description": "To End time in epoch timestamps in milliseconds or relative using Grafana time units.", - "example": "now", - "type": "string" - } - }, - "required": [ - "from", - "to", - "queries" - ], - "type": "object" - }, "MonthRange": { "properties": { "Begin": { @@ -1425,34 +1359,6 @@ }, "type": "object" }, - "NavLink": { - "properties": { - "id": { - "type": "string" - }, - "target": { - "type": "string" - }, - "text": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "NavbarPreference": { - "properties": { - "savedItems": { - "items": { - "$ref": "#/definitions/NavLink" - }, - "type": "array" - } - }, - "type": "object" - }, "NotFound": { "type": "object" }, @@ -1668,53 +1574,9 @@ }, "type": "object" }, - "PatchPrefsCmd": { - "properties": { - "homeDashboardId": { - "default": 0, - "description": "The numerical :id of a favorited dashboard", - "format": "int64", - "type": "integer" - }, - "homeDashboardUID": { - "type": "string" - }, - "locale": { - "type": "string" - }, - "navbar": { - "$ref": "#/definitions/NavbarPreference" - }, - "queryHistory": { - "$ref": "#/definitions/QueryHistoryPreference" - }, - "theme": { - "enum": [ - "light", - "dark" - ], - "type": "string" - }, - "timezone": { - "enum": [ - "utc", - "browser" - ], - "type": "string" - }, - "weekStart": { - "type": "string" - } - }, - "type": "object" - }, "PermissionDenied": { "type": "object" }, - "PermissionType": { - "format": "int64", - "type": "integer" - }, "Point": { "properties": { "T": { @@ -2036,14 +1898,6 @@ }, "type": "object" }, - "QueryHistoryPreference": { - "properties": { - "homeTab": { - "type": "string" - } - }, - "type": "object" - }, "Receiver": { "properties": { "email_configs": { @@ -2738,6 +2592,7 @@ "type": "object" }, "URL": { + "description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.", "properties": { "ForceQuery": { "type": "boolean" @@ -2770,58 +2625,7 @@ "$ref": "#/definitions/Userinfo" } }, - "title": "URL is a custom URL type that allows validation at configuration load time.", - "type": "object" - }, - "UpdateDashboardAclCommand": { - "properties": { - "items": { - "items": { - "$ref": "#/definitions/DashboardAclUpdateItem" - }, - "type": "array" - } - }, - "type": "object" - }, - "UpdatePrefsCmd": { - "properties": { - "homeDashboardId": { - "default": 0, - "description": "The numerical :id of a favorited dashboard", - "format": "int64", - "type": "integer" - }, - "homeDashboardUID": { - "type": "string" - }, - "locale": { - "type": "string" - }, - "navbar": { - "$ref": "#/definitions/NavbarPreference" - }, - "queryHistory": { - "$ref": "#/definitions/QueryHistoryPreference" - }, - "theme": { - "enum": [ - "light", - "dark" - ], - "type": "string" - }, - "timezone": { - "enum": [ - "utc", - "browser" - ], - "type": "string" - }, - "weekStart": { - "type": "string" - } - }, + "title": "A URL represents a parsed URL (technically, a URI reference).", "type": "object" }, "Userinfo": { @@ -2991,7 +2795,6 @@ "type": "object" }, "alertGroup": { - "description": "AlertGroup alert group", "properties": { "alerts": { "description": "alerts", @@ -3015,7 +2818,6 @@ "type": "object" }, "alertGroups": { - "description": "AlertGroups alert groups", "items": { "$ref": "#/definitions/alertGroup" }, @@ -3123,6 +2925,7 @@ "$ref": "#/definitions/Duration" }, "gettableAlert": { + "description": "GettableAlert gettable alert", "properties": { "annotations": { "$ref": "#/definitions/labelSet" @@ -3184,7 +2987,6 @@ "type": "array" }, "gettableSilence": { - "description": "GettableSilence gettable silence", "properties": { "comment": { "description": "comment", @@ -3344,7 +3146,6 @@ "type": "array" }, "postableSilence": { - "description": "PostableSilence postable silence", "properties": { "comment": { "description": "comment", @@ -3382,6 +3183,7 @@ "type": "object" }, "receiver": { + "description": "Receiver receiver", "properties": { "name": { "description": "name", @@ -3730,6 +3532,35 @@ } }, "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}": { + "get": { + "operationId": "RouteGetAlertRuleGroup", + "parameters": [ + { + "in": "path", + "name": "FolderUID", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "Group", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "$ref": "#/responses/AlertRuleGroup" + }, + "404": { + "description": " Not found." + } + }, + "summary": "Get a rule group.", + "tags": [ + "provisioning" + ] + }, "put": { "consumes": [ "application/json" @@ -3752,15 +3583,15 @@ "in": "body", "name": "Body", "schema": { - "$ref": "#/definitions/AlertRuleGroup" + "$ref": "#/definitions/AlertRuleGroupMetadata" } } ], "responses": { "200": { - "description": "AlertRuleGroup", + "description": "AlertRuleGroupMetadata", "schema": { - "$ref": "#/definitions/AlertRuleGroup" + "$ref": "#/definitions/AlertRuleGroupMetadata" } }, "400": { diff --git a/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go b/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go index 44a7df7491e..d54f27090bd 100644 --- a/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go +++ b/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go @@ -135,6 +135,14 @@ func NewAlertRule(rule models.AlertRule, provenance models.Provenance) AlertRule } } +// swagger:route GET /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} provisioning stable RouteGetAlertRuleGroup +// +// Get a rule group. +// +// Responses: +// 200: AlertRuleGroup +// 404: description: Not found. + // swagger:route PUT /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} provisioning stable RoutePutAlertRuleGroup // // Update the interval of a rule group. @@ -143,16 +151,16 @@ func NewAlertRule(rule models.AlertRule, provenance models.Provenance) AlertRule // - application/json // // Responses: -// 200: AlertRuleGroup +// 200: AlertRuleGroupMetadata // 400: ValidationError -// swagger:parameters RoutePutAlertRuleGroup +// swagger:parameters RouteGetAlertRuleGroup RoutePutAlertRuleGroup type FolderUIDPathParam struct { // in:path FolderUID string `json:"FolderUID"` } -// swagger:parameters RoutePutAlertRuleGroup +// swagger:parameters RouteGetAlertRuleGroup RoutePutAlertRuleGroup type RuleGroupPathParam struct { // in:path Group string `json:"Group"` @@ -161,9 +169,16 @@ type RuleGroupPathParam struct { // swagger:parameters RoutePutAlertRuleGroup type AlertRuleGroupPayload struct { // in:body - Body AlertRuleGroup + Body AlertRuleGroupMetadata +} + +type AlertRuleGroupMetadata struct { + Interval int64 `json:"interval"` } type AlertRuleGroup struct { - Interval int64 `json:"interval"` + Title string `json:"title"` + FolderUID string `json:"folderUid"` + Interval int64 `json:"interval"` + Rules []models.AlertRule `json:"rules"` } diff --git a/pkg/services/ngalert/api/tooling/post.json b/pkg/services/ngalert/api/tooling/post.json index 5f1dc277dda..05c7dbeb931 100644 --- a/pkg/services/ngalert/api/tooling/post.json +++ b/pkg/services/ngalert/api/tooling/post.json @@ -308,7 +308,7 @@ ], "type": "object" }, - "AlertRuleGroup": { + "AlertRuleGroupMetadata": { "properties": { "interval": { "format": "int64", @@ -2592,6 +2592,7 @@ "type": "object" }, "URL": { + "description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.", "properties": { "ForceQuery": { "type": "boolean" @@ -2624,7 +2625,7 @@ "$ref": "#/definitions/Userinfo" } }, - "title": "URL is a custom URL type that allows validation at configuration load time.", + "title": "A URL represents a parsed URL (technically, a URI reference).", "type": "object" }, "Userinfo": { @@ -2794,6 +2795,7 @@ "type": "object" }, "alertGroup": { + "description": "AlertGroup alert group", "properties": { "alerts": { "description": "alerts", @@ -2988,6 +2990,7 @@ "type": "array" }, "gettableSilence": { + "description": "GettableSilence gettable silence", "properties": { "comment": { "description": "comment", @@ -3146,6 +3149,7 @@ "type": "array" }, "postableSilence": { + "description": "PostableSilence postable silence", "properties": { "comment": { "description": "comment", @@ -5157,6 +5161,35 @@ } }, "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}": { + "get": { + "operationId": "RouteGetAlertRuleGroup", + "parameters": [ + { + "in": "path", + "name": "FolderUID", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "Group", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "$ref": "#/responses/AlertRuleGroup" + }, + "404": { + "description": " Not found." + } + }, + "summary": "Get a rule group.", + "tags": [ + "provisioning" + ] + }, "put": { "consumes": [ "application/json" @@ -5179,15 +5212,15 @@ "in": "body", "name": "Body", "schema": { - "$ref": "#/definitions/AlertRuleGroup" + "$ref": "#/definitions/AlertRuleGroupMetadata" } } ], "responses": { "200": { - "description": "AlertRuleGroup", + "description": "AlertRuleGroupMetadata", "schema": { - "$ref": "#/definitions/AlertRuleGroup" + "$ref": "#/definitions/AlertRuleGroupMetadata" } }, "400": { diff --git a/pkg/services/ngalert/api/tooling/spec.json b/pkg/services/ngalert/api/tooling/spec.json index 61d42c493db..6878508f9b7 100644 --- a/pkg/services/ngalert/api/tooling/spec.json +++ b/pkg/services/ngalert/api/tooling/spec.json @@ -1893,6 +1893,36 @@ } }, "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}": { + "get": { + "tags": [ + "provisioning", + "stable" + ], + "summary": "Get a rule group.", + "operationId": "RouteGetAlertRuleGroup", + "parameters": [ + { + "type": "string", + "name": "FolderUID", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "Group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/AlertRuleGroup" + }, + "404": { + "description": " Not found." + } + } + }, "put": { "consumes": [ "application/json" @@ -1920,15 +1950,15 @@ "name": "Body", "in": "body", "schema": { - "$ref": "#/definitions/AlertRuleGroup" + "$ref": "#/definitions/AlertRuleGroupMetadata" } } ], "responses": { "200": { - "description": "AlertRuleGroup", + "description": "AlertRuleGroupMetadata", "schema": { - "$ref": "#/definitions/AlertRuleGroup" + "$ref": "#/definitions/AlertRuleGroupMetadata" } }, "400": { @@ -2629,7 +2659,7 @@ } } }, - "AlertRuleGroup": { + "AlertRuleGroupMetadata": { "type": "object", "properties": { "interval": { @@ -4917,8 +4947,9 @@ } }, "URL": { + "description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.", "type": "object", - "title": "URL is a custom URL type that allows validation at configuration load time.", + "title": "A URL represents a parsed URL (technically, a URI reference).", "properties": { "ForceQuery": { "type": "boolean" @@ -5119,6 +5150,7 @@ } }, "alertGroup": { + "description": "AlertGroup alert group", "type": "object", "required": [ "alerts", @@ -5143,7 +5175,6 @@ "$ref": "#/definitions/alertGroup" }, "alertGroups": { - "description": "AlertGroups alert groups", "type": "array", "items": { "$ref": "#/definitions/alertGroup" @@ -5252,7 +5283,6 @@ "$ref": "#/definitions/Duration" }, "gettableAlert": { - "description": "GettableAlert gettable alert", "type": "object", "required": [ "labels", @@ -5309,7 +5339,6 @@ "$ref": "#/definitions/gettableAlert" }, "gettableAlerts": { - "description": "GettableAlerts gettable alerts", "type": "array", "items": { "$ref": "#/definitions/gettableAlert" @@ -5515,6 +5544,7 @@ "$ref": "#/definitions/postableSilence" }, "receiver": { + "description": "Receiver receiver", "type": "object", "required": [ "name" diff --git a/pkg/services/ngalert/provisioning/alert_rules.go b/pkg/services/ngalert/provisioning/alert_rules.go index 04e4e9a8987..5d618317eda 100644 --- a/pkg/services/ngalert/provisioning/alert_rules.go +++ b/pkg/services/ngalert/provisioning/alert_rules.go @@ -7,6 +7,7 @@ import ( "time" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/util" @@ -89,6 +90,32 @@ func (service *AlertRuleService) CreateAlertRule(ctx context.Context, rule model return rule, nil } +func (service *AlertRuleService) GetRuleGroup(ctx context.Context, orgID int64, folder, group string) (definitions.AlertRuleGroup, error) { + q := models.ListAlertRulesQuery{ + OrgID: orgID, + NamespaceUIDs: []string{folder}, + RuleGroup: group, + } + if err := service.ruleStore.ListAlertRules(ctx, &q); err != nil { + return definitions.AlertRuleGroup{}, err + } + if len(q.Result) == 0 { + return definitions.AlertRuleGroup{}, store.ErrAlertRuleGroupNotFound + } + res := definitions.AlertRuleGroup{ + Title: q.Result[0].RuleGroup, + FolderUID: q.Result[0].NamespaceUID, + Interval: q.Result[0].IntervalSeconds, + Rules: []models.AlertRule{}, + } + for _, r := range q.Result { + if r != nil { + res.Rules = append(res.Rules, *r) + } + } + return res, nil +} + // UpdateRuleGroup will update the interval for all rules in the group. func (service *AlertRuleService) UpdateRuleGroup(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string, interval int64) error { if err := models.ValidateRuleGroupInterval(interval, service.baseIntervalSeconds); err != nil {