mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Time intervals API (read only endpoints) (#81672)
* declare new API and models GettableTimeIntervals, PostableTimeIntervals * add new actions alert.notifications.time-intervals:read and alert.notifications.time-intervals:write. * update existing alerting roles with the read action. Add to all alerting roles. * add integration tests
This commit is contained in:
parent
7e939401dc
commit
d1073deefd
@ -445,6 +445,10 @@ const (
|
||||
ActionAlertingNotificationsRead = "alert.notifications:read"
|
||||
ActionAlertingNotificationsWrite = "alert.notifications:write"
|
||||
|
||||
// Alerting notifications time interval actions
|
||||
ActionAlertingNotificationsTimeIntervalsRead = "alert.notifications.time-intervals:read"
|
||||
ActionAlertingNotificationsTimeIntervalsWrite = "alert.notifications.time-intervals:write"
|
||||
|
||||
// External alerting rule actions. We can only narrow it down to writes or reads, as we don't control the atomicity in the external system.
|
||||
ActionAlertingRuleExternalWrite = "alert.rules.external:write"
|
||||
ActionAlertingRuleExternalRead = "alert.rules.external:read"
|
||||
|
@ -25,6 +25,9 @@ var (
|
||||
Action: accesscontrol.ActionAlertingRuleExternalRead,
|
||||
Scope: datasources.ScopeAll,
|
||||
},
|
||||
{
|
||||
Action: accesscontrol.ActionAlertingNotificationsTimeIntervalsRead, // This is needed for simplified notification policies
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -109,6 +112,9 @@ var (
|
||||
Action: accesscontrol.ActionAlertingNotificationsExternalRead,
|
||||
Scope: datasources.ScopeAll,
|
||||
},
|
||||
{
|
||||
Action: accesscontrol.ActionAlertingNotificationsTimeIntervalsRead,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -152,6 +152,8 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
|
||||
hist: api.Historian,
|
||||
}), m)
|
||||
|
||||
api.RegisterNotificationsApiEndpoints(NewNotificationsApi(api.MuteTimings), m)
|
||||
|
||||
// Inject upgrade endpoints if legacy alerting is enabled and the feature flag is enabled.
|
||||
if !api.Cfg.UnifiedAlerting.IsEnabled() && api.FeatureManager.IsEnabledGlobally(featuremgmt.FlagAlertingPreviewUpgrade) {
|
||||
api.RegisterUpgradeApiEndpoints(NewUpgradeApi(NewUpgradeSrc(
|
||||
|
@ -238,6 +238,9 @@ func (api *API) authorize(method, path string) web.Handler {
|
||||
http.MethodDelete + "/api/v1/provisioning/alert-rules/{UID}",
|
||||
http.MethodPut + "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}":
|
||||
eval = ac.EvalPermission(ac.ActionAlertingProvisioningWrite) // organization scope
|
||||
case http.MethodGet + "/api/v1/notifications/time-intervals/{name}",
|
||||
http.MethodGet + "/api/v1/notifications/time-intervals":
|
||||
eval = ac.EvalAny(ac.EvalPermission(ac.ActionAlertingNotificationsRead), ac.EvalPermission(ac.ActionAlertingNotificationsTimeIntervalsRead), ac.EvalPermission(ac.ActionAlertingProvisioningRead))
|
||||
}
|
||||
|
||||
if eval != nil {
|
||||
|
@ -40,7 +40,7 @@ func TestAuthorize(t *testing.T) {
|
||||
}
|
||||
paths[p] = methods
|
||||
}
|
||||
require.Len(t, paths, 60)
|
||||
require.Len(t, paths, 62)
|
||||
|
||||
ac := acmock.New()
|
||||
api := &API{AccessControl: ac}
|
||||
|
62
pkg/services/ngalert/api/generated_base_api_notifications.go
Normal file
62
pkg/services/ngalert/api/generated_base_api_notifications.go
Normal file
@ -0,0 +1,62 @@
|
||||
/*Package api contains base API implementation of unified alerting
|
||||
*
|
||||
*Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*
|
||||
*Do not manually edit these files, please find ngalert/api/swagger-codegen/ for commands on how to generate them.
|
||||
*/
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/middleware/requestmeta"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
type NotificationsApi interface {
|
||||
RouteNotificationsGetTimeInterval(*contextmodel.ReqContext) response.Response
|
||||
RouteNotificationsGetTimeIntervals(*contextmodel.ReqContext) response.Response
|
||||
}
|
||||
|
||||
func (f *NotificationsApiHandler) RouteNotificationsGetTimeInterval(ctx *contextmodel.ReqContext) response.Response {
|
||||
// Parse Path Parameters
|
||||
nameParam := web.Params(ctx.Req)[":name"]
|
||||
return f.handleRouteNotificationsGetTimeInterval(ctx, nameParam)
|
||||
}
|
||||
func (f *NotificationsApiHandler) RouteNotificationsGetTimeIntervals(ctx *contextmodel.ReqContext) response.Response {
|
||||
return f.handleRouteNotificationsGetTimeIntervals(ctx)
|
||||
}
|
||||
|
||||
func (api *API) RegisterNotificationsApiEndpoints(srv NotificationsApi, m *metrics.API) {
|
||||
api.RouteRegister.Group("", func(group routing.RouteRegister) {
|
||||
group.Get(
|
||||
toMacaronPath("/api/v1/notifications/time-intervals/{name}"),
|
||||
requestmeta.SetOwner(requestmeta.TeamAlerting),
|
||||
requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow),
|
||||
api.authorize(http.MethodGet, "/api/v1/notifications/time-intervals/{name}"),
|
||||
metrics.Instrument(
|
||||
http.MethodGet,
|
||||
"/api/v1/notifications/time-intervals/{name}",
|
||||
api.Hooks.Wrap(srv.RouteNotificationsGetTimeInterval),
|
||||
m,
|
||||
),
|
||||
)
|
||||
group.Get(
|
||||
toMacaronPath("/api/v1/notifications/time-intervals"),
|
||||
requestmeta.SetOwner(requestmeta.TeamAlerting),
|
||||
requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow),
|
||||
api.authorize(http.MethodGet, "/api/v1/notifications/time-intervals"),
|
||||
metrics.Instrument(
|
||||
http.MethodGet,
|
||||
"/api/v1/notifications/time-intervals",
|
||||
api.Hooks.Wrap(srv.RouteNotificationsGetTimeIntervals),
|
||||
m,
|
||||
),
|
||||
)
|
||||
}, middleware.ReqSignedIn)
|
||||
}
|
34
pkg/services/ngalert/api/notifications.go
Normal file
34
pkg/services/ngalert/api/notifications.go
Normal file
@ -0,0 +1,34 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
)
|
||||
|
||||
type NotificationsApiHandler struct {
|
||||
muteTimingService MuteTimingService
|
||||
}
|
||||
|
||||
func NewNotificationsApi(muteTimingService MuteTimingService) NotificationsApi {
|
||||
return &NotificationsApiHandler{
|
||||
muteTimingService: muteTimingService,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *NotificationsApiHandler) handleRouteNotificationsGetTimeInterval(ctx *contextmodel.ReqContext, name string) response.Response {
|
||||
model, err := f.muteTimingService.GetMuteTiming(ctx.Req.Context(), name, ctx.OrgID)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
}
|
||||
return response.JSON(http.StatusOK, model) // TODO convert to timing interval
|
||||
}
|
||||
|
||||
func (f *NotificationsApiHandler) handleRouteNotificationsGetTimeIntervals(ctx *contextmodel.ReqContext) response.Response {
|
||||
model, err := f.muteTimingService.GetMuteTimings(ctx.Req.Context(), ctx.OrgID)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
}
|
||||
return response.JSON(http.StatusOK, model) // TODO convert to timing interval
|
||||
}
|
@ -1256,6 +1256,15 @@
|
||||
},
|
||||
"typeVersion": {
|
||||
"$ref": "#/definitions/FrameTypeVersion"
|
||||
},
|
||||
"uniqueRowIdFields": {
|
||||
"description": "Array of field indices which values create a unique id for each row. Ideally this should be globally unique ID\nbut that isn't guarantied. Should help with keeping track and deduplicating rows in visualizations, especially\nwith streaming data with frequent updates.",
|
||||
"example": "TraceID in Tempo, table name + primary key in SQL",
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "FrameMeta matches:",
|
||||
@ -1667,6 +1676,23 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"GettableTimeIntervals": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "#/definitions/Provenance"
|
||||
},
|
||||
"time_intervals": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"GettableUserConfig": {
|
||||
"properties": {
|
||||
"alertmanager_config": {
|
||||
@ -2816,6 +2842,20 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"PostableTimeIntervals": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"time_intervals": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"PostableUserConfig": {
|
||||
"properties": {
|
||||
"alertmanager_config": {
|
||||
@ -4166,6 +4206,55 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeIntervalItem": {
|
||||
"properties": {
|
||||
"days_of_month": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"months": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"times": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalTimeRange"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"weekdays": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"years": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeIntervalTimeRange": {
|
||||
"properties": {
|
||||
"end_time": {
|
||||
"type": "string"
|
||||
},
|
||||
"start_time": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeRange": {
|
||||
"description": "Redefining this to avoid an import cycle",
|
||||
"properties": {
|
||||
@ -4181,7 +4270,6 @@
|
||||
"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 the EscapedPath method, which preserves\nthe original encoding of Path.\n\nThe RawPath field is an optional field which is only set when the default\nencoding of Path is different from the escaped path. See the EscapedPath method\nfor more details.\n\nURL's String method uses the EscapedPath method to obtain the path.",
|
||||
"properties": {
|
||||
"ForceQuery": {
|
||||
"type": "boolean"
|
||||
@ -4217,7 +4305,7 @@
|
||||
"$ref": "#/definitions/Userinfo"
|
||||
}
|
||||
},
|
||||
"title": "A URL represents a parsed URL (technically, a URI reference).",
|
||||
"title": "URL is a custom URL type that allows validation at configuration load time.",
|
||||
"type": "object"
|
||||
},
|
||||
"UpdateRuleGroupResponse": {
|
||||
@ -4613,6 +4701,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"properties": {
|
||||
"comment": {
|
||||
"description": "comment",
|
||||
@ -4661,13 +4750,13 @@
|
||||
"type": "object"
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"integration": {
|
||||
"description": "Integration integration",
|
||||
"properties": {
|
||||
"lastNotifyAttempt": {
|
||||
"description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time",
|
||||
@ -4849,7 +4938,6 @@
|
||||
"type": "object"
|
||||
},
|
||||
"receiver": {
|
||||
"description": "Receiver receiver",
|
||||
"properties": {
|
||||
"active": {
|
||||
"description": "active",
|
||||
@ -4969,6 +5057,61 @@
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/v1/notifications/time-intervals": {
|
||||
"get": {
|
||||
"description": "Get all the time intervals",
|
||||
"operationId": "RouteNotificationsGetTimeIntervals",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/GetAllIntervalsResponse"
|
||||
},
|
||||
"403": {
|
||||
"description": "ForbiddenError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ForbiddenError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals/{name}": {
|
||||
"get": {
|
||||
"operationId": "RouteNotificationsGetTimeInterval",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Time interval name",
|
||||
"in": "path",
|
||||
"name": "name",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/GetIntervalsByNameResponse"
|
||||
},
|
||||
"403": {
|
||||
"description": "ForbiddenError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ForbiddenError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "NotFound",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/NotFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"summary": "Get a time interval by name.",
|
||||
"tags": [
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/provisioning/alert-rules": {
|
||||
"get": {
|
||||
"operationId": "RouteGetAlertRules",
|
||||
@ -6010,6 +6153,21 @@
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"GetAllIntervalsResponse": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/GettableTimeIntervals"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"GetIntervalsByNameResponse": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/GettableTimeIntervals"
|
||||
}
|
||||
},
|
||||
"GettableHistoricUserConfigs": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
|
@ -0,0 +1,64 @@
|
||||
package definitions
|
||||
|
||||
// swagger:route GET /v1/notifications/time-intervals notifications stable RouteNotificationsGetTimeIntervals
|
||||
//
|
||||
// Get all the time intervals
|
||||
//
|
||||
// Responses:
|
||||
// 200: GetAllIntervalsResponse
|
||||
// 403: ForbiddenError
|
||||
|
||||
// swagger:route GET /v1/notifications/time-intervals/{name} notifications stable RouteNotificationsGetTimeInterval
|
||||
//
|
||||
// Get a time interval by name.
|
||||
//
|
||||
// Responses:
|
||||
// 200: GetIntervalsByNameResponse
|
||||
// 404: NotFound
|
||||
// 403: ForbiddenError
|
||||
|
||||
// swagger:parameters stable RouteNotificationsGetTimeInterval
|
||||
type RouteTimeIntervalNameParam struct {
|
||||
// Time interval name
|
||||
// in:path
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// swagger:response GetAllIntervalsResponse
|
||||
type GetAllIntervalsResponse struct {
|
||||
// in:body
|
||||
Body []GettableTimeIntervals
|
||||
}
|
||||
|
||||
// swagger:response GetIntervalsByNameResponse
|
||||
type GetIntervalsByNameResponse struct {
|
||||
// in:body
|
||||
Body GettableTimeIntervals
|
||||
}
|
||||
|
||||
// swagger:model
|
||||
type PostableTimeIntervals struct {
|
||||
Name string `json:"name" hcl:"name"`
|
||||
TimeIntervals []TimeIntervalItem `json:"time_intervals" hcl:"intervals,block"`
|
||||
}
|
||||
|
||||
type TimeIntervalItem struct {
|
||||
Times []TimeIntervalTimeRange `json:"times,omitempty" hcl:"times,block"`
|
||||
Weekdays *[]string `json:"weekdays,omitempty" hcl:"weekdays"`
|
||||
DaysOfMonth *[]string `json:"days_of_month,omitempty" hcl:"days_of_month"`
|
||||
Months *[]string `json:"months,omitempty" hcl:"months"`
|
||||
Years *[]string `json:"years,omitempty" hcl:"years"`
|
||||
Location *string `json:"location,omitempty" hcl:"location"`
|
||||
}
|
||||
|
||||
type TimeIntervalTimeRange struct {
|
||||
StartMinute string `json:"start_time" hcl:"start"`
|
||||
EndMinute string `json:"end_time" hcl:"end"`
|
||||
}
|
||||
|
||||
// swagger:model
|
||||
type GettableTimeIntervals struct {
|
||||
Name string `json:"name" hcl:"name"`
|
||||
TimeIntervals []TimeIntervalItem `json:"time_intervals" hcl:"intervals,block"`
|
||||
Provenance Provenance `json:"provenance,omitempty"`
|
||||
}
|
@ -1256,6 +1256,15 @@
|
||||
},
|
||||
"typeVersion": {
|
||||
"$ref": "#/definitions/FrameTypeVersion"
|
||||
},
|
||||
"uniqueRowIdFields": {
|
||||
"description": "Array of field indices which values create a unique id for each row. Ideally this should be globally unique ID\nbut that isn't guarantied. Should help with keeping track and deduplicating rows in visualizations, especially\nwith streaming data with frequent updates.",
|
||||
"example": "TraceID in Tempo, table name + primary key in SQL",
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "FrameMeta matches:",
|
||||
@ -1667,6 +1676,23 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"GettableTimeIntervals": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "#/definitions/Provenance"
|
||||
},
|
||||
"time_intervals": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"GettableUserConfig": {
|
||||
"properties": {
|
||||
"alertmanager_config": {
|
||||
@ -2816,6 +2842,20 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"PostableTimeIntervals": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"time_intervals": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"PostableUserConfig": {
|
||||
"properties": {
|
||||
"alertmanager_config": {
|
||||
@ -4166,6 +4206,55 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeIntervalItem": {
|
||||
"properties": {
|
||||
"days_of_month": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"months": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"times": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalTimeRange"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"weekdays": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"years": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeIntervalTimeRange": {
|
||||
"properties": {
|
||||
"end_time": {
|
||||
"type": "string"
|
||||
},
|
||||
"start_time": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeRange": {
|
||||
"description": "Redefining this to avoid an import cycle",
|
||||
"properties": {
|
||||
@ -4181,6 +4270,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 the EscapedPath method, which preserves\nthe original encoding of Path.\n\nThe RawPath field is an optional field which is only set when the default\nencoding of Path is different from the escaped path. See the EscapedPath method\nfor more details.\n\nURL's String method uses the EscapedPath method to obtain the path.",
|
||||
"properties": {
|
||||
"ForceQuery": {
|
||||
"type": "boolean"
|
||||
@ -4216,7 +4306,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"
|
||||
},
|
||||
"UpdateRuleGroupResponse": {
|
||||
@ -4422,6 +4512,7 @@
|
||||
"type": "object"
|
||||
},
|
||||
"alertGroup": {
|
||||
"description": "AlertGroup alert group",
|
||||
"properties": {
|
||||
"alerts": {
|
||||
"description": "alerts",
|
||||
@ -4445,7 +4536,6 @@
|
||||
"type": "object"
|
||||
},
|
||||
"alertGroups": {
|
||||
"description": "AlertGroups alert groups",
|
||||
"items": {
|
||||
"$ref": "#/definitions/alertGroup"
|
||||
},
|
||||
@ -4550,7 +4640,6 @@
|
||||
"type": "object"
|
||||
},
|
||||
"gettableAlert": {
|
||||
"description": "GettableAlert gettable alert",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"$ref": "#/definitions/labelSet"
|
||||
@ -4612,6 +4701,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"properties": {
|
||||
"comment": {
|
||||
"description": "comment",
|
||||
@ -4660,14 +4750,12 @@
|
||||
"type": "object"
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"integration": {
|
||||
"description": "Integration integration",
|
||||
"properties": {
|
||||
"lastNotifyAttempt": {
|
||||
"description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time",
|
||||
@ -4811,7 +4899,6 @@
|
||||
"type": "array"
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"properties": {
|
||||
"comment": {
|
||||
"description": "comment",
|
||||
@ -4849,6 +4936,7 @@
|
||||
"type": "object"
|
||||
},
|
||||
"receiver": {
|
||||
"description": "Receiver receiver",
|
||||
"properties": {
|
||||
"active": {
|
||||
"description": "active",
|
||||
@ -6958,6 +7046,61 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals": {
|
||||
"get": {
|
||||
"description": "Get all the time intervals",
|
||||
"operationId": "RouteNotificationsGetTimeIntervals",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/GetAllIntervalsResponse"
|
||||
},
|
||||
"403": {
|
||||
"description": "ForbiddenError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ForbiddenError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals/{name}": {
|
||||
"get": {
|
||||
"operationId": "RouteNotificationsGetTimeInterval",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Time interval name",
|
||||
"in": "path",
|
||||
"name": "name",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/GetIntervalsByNameResponse"
|
||||
},
|
||||
"403": {
|
||||
"description": "ForbiddenError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ForbiddenError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "NotFound",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/NotFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"summary": "Get a time interval by name.",
|
||||
"tags": [
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/provisioning/alert-rules": {
|
||||
"get": {
|
||||
"operationId": "RouteGetAlertRules",
|
||||
@ -8349,6 +8492,21 @@
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"GetAllIntervalsResponse": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/GettableTimeIntervals"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"GetIntervalsByNameResponse": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/GettableTimeIntervals"
|
||||
}
|
||||
},
|
||||
"GettableHistoricUserConfigs": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
|
@ -2007,6 +2007,63 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals": {
|
||||
"get": {
|
||||
"description": "Get all the time intervals",
|
||||
"tags": [
|
||||
"notifications",
|
||||
"stable"
|
||||
],
|
||||
"operationId": "RouteNotificationsGetTimeIntervals",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/GetAllIntervalsResponse"
|
||||
},
|
||||
"403": {
|
||||
"description": "ForbiddenError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ForbiddenError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals/{name}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"notifications",
|
||||
"stable"
|
||||
],
|
||||
"summary": "Get a time interval by name.",
|
||||
"operationId": "RouteNotificationsGetTimeInterval",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Time interval name",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/GetIntervalsByNameResponse"
|
||||
},
|
||||
"403": {
|
||||
"description": "ForbiddenError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ForbiddenError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "NotFound",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/NotFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/provisioning/alert-rules": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@ -4682,6 +4739,15 @@
|
||||
},
|
||||
"typeVersion": {
|
||||
"$ref": "#/definitions/FrameTypeVersion"
|
||||
},
|
||||
"uniqueRowIdFields": {
|
||||
"description": "Array of field indices which values create a unique id for each row. Ideally this should be globally unique ID\nbut that isn't guarantied. Should help with keeping track and deduplicating rows in visualizations, especially\nwith streaming data with frequent updates.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"example": "TraceID in Tempo, table name + primary key in SQL"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -5091,6 +5157,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"GettableTimeIntervals": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "#/definitions/Provenance"
|
||||
},
|
||||
"time_intervals": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"GettableUserConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -6241,6 +6324,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"PostableTimeIntervals": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"time_intervals": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PostableUserConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -7591,6 +7688,55 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimeIntervalItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"days_of_month": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"months": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"times": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalTimeRange"
|
||||
}
|
||||
},
|
||||
"weekdays": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"years": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimeIntervalTimeRange": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"end_time": {
|
||||
"type": "string"
|
||||
},
|
||||
"start_time": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimeRange": {
|
||||
"description": "Redefining this to avoid an import cycle",
|
||||
"type": "object",
|
||||
@ -7606,8 +7752,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 the EscapedPath method, which preserves\nthe original encoding of Path.\n\nThe RawPath field is an optional field which is only set when the default\nencoding of Path is different from the escaped path. See the EscapedPath method\nfor more details.\n\nURL's String method uses the EscapedPath method to obtain the path.",
|
||||
"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"
|
||||
@ -7847,6 +7994,7 @@
|
||||
}
|
||||
},
|
||||
"alertGroup": {
|
||||
"description": "AlertGroup alert group",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"alerts",
|
||||
@ -7871,7 +8019,6 @@
|
||||
"$ref": "#/definitions/alertGroup"
|
||||
},
|
||||
"alertGroups": {
|
||||
"description": "AlertGroups alert groups",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/alertGroup"
|
||||
@ -7977,7 +8124,6 @@
|
||||
}
|
||||
},
|
||||
"gettableAlert": {
|
||||
"description": "GettableAlert gettable alert",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"labels",
|
||||
@ -8041,6 +8187,7 @@
|
||||
"$ref": "#/definitions/gettableAlerts"
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment",
|
||||
@ -8090,7 +8237,6 @@
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
@ -8098,7 +8244,6 @@
|
||||
"$ref": "#/definitions/gettableSilences"
|
||||
},
|
||||
"integration": {
|
||||
"description": "Integration integration",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
@ -8243,7 +8388,6 @@
|
||||
}
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment",
|
||||
@ -8282,6 +8426,7 @@
|
||||
"$ref": "#/definitions/postableSilence"
|
||||
},
|
||||
"receiver": {
|
||||
"description": "Receiver receiver",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"active",
|
||||
@ -8397,6 +8542,21 @@
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"GetAllIntervalsResponse": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/GettableTimeIntervals"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GetIntervalsByNameResponse": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/GettableTimeIntervals"
|
||||
}
|
||||
},
|
||||
"GettableHistoricUserConfigs": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
|
204
pkg/tests/api/alerting/api_notifications_time_interval_test.go
Normal file
204
pkg/tests/api/alerting/api_notifications_time_interval_test.go
Normal file
@ -0,0 +1,204 @@
|
||||
package alerting
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/timeinterval"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func TestTimeInterval(t *testing.T) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableLegacyAlerting: true,
|
||||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
})
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
createUser(t, store, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleAdmin),
|
||||
Password: "admin",
|
||||
Login: "admin",
|
||||
})
|
||||
|
||||
apiClient := newAlertingApiClient(grafanaListedAddr, "admin", "admin")
|
||||
|
||||
t.Run("default config should return empty list", func(t *testing.T) {
|
||||
mt, status, body := apiClient.GetAllTimeIntervalsWithStatus(t)
|
||||
requireStatusCode(t, http.StatusOK, status, body)
|
||||
require.Empty(t, mt)
|
||||
})
|
||||
|
||||
emptyTimeInterval := definitions.PostableTimeIntervals{
|
||||
Name: "Empty Mute Timing",
|
||||
TimeIntervals: []definitions.TimeIntervalItem{},
|
||||
}
|
||||
|
||||
func() {
|
||||
// TODO replace with Time-Interval later
|
||||
emptyMuteTiming := definitions.MuteTimeInterval{
|
||||
MuteTimeInterval: config.MuteTimeInterval{
|
||||
Name: "Empty Mute Timing",
|
||||
TimeIntervals: []timeinterval.TimeInterval{},
|
||||
},
|
||||
}
|
||||
|
||||
// TODO replace with create interval API
|
||||
// t.Run("should create a new mute timing without any intervals", func(t *testing.T) {
|
||||
mt, status, body := apiClient.CreateMuteTimingWithStatus(t, emptyMuteTiming)
|
||||
requireStatusCode(t, http.StatusCreated, status, body)
|
||||
require.Equal(t, emptyMuteTiming.MuteTimeInterval, mt.MuteTimeInterval)
|
||||
require.EqualValues(t, models.ProvenanceAPI, mt.Provenance)
|
||||
// })
|
||||
|
||||
anotherMuteTiming := definitions.MuteTimeInterval{
|
||||
MuteTimeInterval: config.MuteTimeInterval{
|
||||
Name: "Not Empty Mute Timing",
|
||||
TimeIntervals: []timeinterval.TimeInterval{
|
||||
{
|
||||
Times: []timeinterval.TimeRange{
|
||||
{
|
||||
StartMinute: 10,
|
||||
EndMinute: 45,
|
||||
},
|
||||
},
|
||||
Weekdays: []timeinterval.WeekdayRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 0,
|
||||
End: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 4,
|
||||
End: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
DaysOfMonth: []timeinterval.DayOfMonthRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 1,
|
||||
End: 7,
|
||||
},
|
||||
},
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 14,
|
||||
End: 28,
|
||||
},
|
||||
},
|
||||
},
|
||||
Months: []timeinterval.MonthRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 1,
|
||||
End: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
Years: []timeinterval.YearRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 2024,
|
||||
End: 2025,
|
||||
},
|
||||
},
|
||||
},
|
||||
Location: &timeinterval.Location{
|
||||
Location: time.UTC,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// t.Run("should create a new mute timing with some settings", func(t *testing.T) {
|
||||
mt, status, body = apiClient.CreateMuteTimingWithStatus(t, anotherMuteTiming)
|
||||
requireStatusCode(t, http.StatusCreated, status, body)
|
||||
require.Equal(t, anotherMuteTiming.MuteTimeInterval, mt.MuteTimeInterval)
|
||||
require.EqualValues(t, models.ProvenanceAPI, mt.Provenance)
|
||||
// })
|
||||
}()
|
||||
|
||||
anotherTimeInterval := definitions.PostableTimeIntervals{
|
||||
Name: "Not Empty Mute Timing",
|
||||
TimeIntervals: []definitions.TimeIntervalItem{
|
||||
{
|
||||
Times: []definitions.TimeIntervalTimeRange{
|
||||
{
|
||||
StartMinute: "00:10",
|
||||
EndMinute: "00:45",
|
||||
},
|
||||
},
|
||||
Weekdays: util.Pointer([]string{
|
||||
"sunday:tuesday",
|
||||
"thursday:friday",
|
||||
}),
|
||||
DaysOfMonth: util.Pointer([]string{
|
||||
"1:7",
|
||||
"14:28",
|
||||
}),
|
||||
Months: util.Pointer([]string{
|
||||
"1:5",
|
||||
}),
|
||||
Years: util.Pointer([]string{
|
||||
"2024:2025",
|
||||
}),
|
||||
Location: util.Pointer("UTC"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("should return time interval by name", func(t *testing.T) {
|
||||
ti, status, body := apiClient.GetTimeIntervalByNameWithStatus(t, emptyTimeInterval.Name)
|
||||
requireStatusCode(t, http.StatusOK, status, body)
|
||||
require.Equal(t, emptyTimeInterval.TimeIntervals, ti.TimeIntervals)
|
||||
require.Equal(t, emptyTimeInterval.Name, ti.Name)
|
||||
require.EqualValues(t, models.ProvenanceAPI, ti.Provenance)
|
||||
|
||||
ti, status, body = apiClient.GetTimeIntervalByNameWithStatus(t, anotherTimeInterval.Name)
|
||||
requireStatusCode(t, http.StatusOK, status, body)
|
||||
require.Equal(t, anotherTimeInterval.TimeIntervals, ti.TimeIntervals)
|
||||
require.Equal(t, anotherTimeInterval.Name, ti.Name)
|
||||
require.EqualValues(t, models.ProvenanceAPI, ti.Provenance)
|
||||
})
|
||||
|
||||
t.Run("should return NotFound if time interval does not exist", func(t *testing.T) {
|
||||
_, status, body := apiClient.GetTimeIntervalByNameWithStatus(t, "some-missing-timing")
|
||||
requireStatusCode(t, http.StatusNotFound, status, body)
|
||||
})
|
||||
|
||||
t.Run("should return all mute timings", func(t *testing.T) {
|
||||
mt, status, body := apiClient.GetAllTimeIntervalsWithStatus(t)
|
||||
requireStatusCode(t, http.StatusOK, status, body)
|
||||
require.Len(t, mt, 2)
|
||||
|
||||
slices.SortFunc(mt, func(a, b definitions.GettableTimeIntervals) int {
|
||||
return strings.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
require.Equal(t, emptyTimeInterval.TimeIntervals, mt[0].TimeIntervals)
|
||||
require.Equal(t, emptyTimeInterval.Name, mt[0].Name)
|
||||
require.EqualValues(t, models.ProvenanceAPI, mt[0].Provenance)
|
||||
|
||||
require.Equal(t, anotherTimeInterval.TimeIntervals, mt[1].TimeIntervals)
|
||||
require.Equal(t, anotherTimeInterval.Name, mt[1].Name)
|
||||
require.EqualValues(t, models.ProvenanceAPI, mt[1].Provenance)
|
||||
})
|
||||
}
|
@ -756,6 +756,24 @@ func (a apiClient) GetRuleHistoryWithStatus(t *testing.T, ruleUID string) (data.
|
||||
return sendRequest[data.Frame](t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func (a apiClient) GetAllTimeIntervalsWithStatus(t *testing.T) ([]apimodels.GettableTimeIntervals, int, string) {
|
||||
t.Helper()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/notifications/time-intervals", a.url), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
return sendRequest[[]apimodels.GettableTimeIntervals](t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func (a apiClient) GetTimeIntervalByNameWithStatus(t *testing.T, name string) (apimodels.GettableTimeIntervals, int, string) {
|
||||
t.Helper()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/notifications/time-intervals/%s", a.url, name), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
return sendRequest[apimodels.GettableTimeIntervals](t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func sendRequest[T any](t *testing.T, req *http.Request, successStatusCode int) (T, int, string) {
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
|
@ -10362,6 +10362,61 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals": {
|
||||
"get": {
|
||||
"description": "Get all the time intervals",
|
||||
"tags": [
|
||||
"notifications"
|
||||
],
|
||||
"operationId": "RouteNotificationsGetTimeIntervals",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/GetAllIntervalsResponse"
|
||||
},
|
||||
"403": {
|
||||
"description": "ForbiddenError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ForbiddenError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals/{name}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"notifications"
|
||||
],
|
||||
"summary": "Get a time interval by name.",
|
||||
"operationId": "RouteNotificationsGetTimeInterval",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Time interval name",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/GetIntervalsByNameResponse"
|
||||
},
|
||||
"403": {
|
||||
"description": "ForbiddenError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ForbiddenError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "NotFound",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/NotFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/provisioning/alert-rules": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@ -15533,6 +15588,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"GettableTimeIntervals": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "#/definitions/Provenance"
|
||||
},
|
||||
"time_intervals": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"GettableUserConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -17781,6 +17853,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"PostableTimeIntervals": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"time_intervals": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PostableUserConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -20448,6 +20534,55 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimeIntervalItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"days_of_month": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"months": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"times": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TimeIntervalTimeRange"
|
||||
}
|
||||
},
|
||||
"weekdays": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"years": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimeIntervalTimeRange": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"end_time": {
|
||||
"type": "string"
|
||||
},
|
||||
"start_time": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimeRange": {
|
||||
"description": "Redefining this to avoid an import cycle",
|
||||
"type": "object",
|
||||
@ -21719,6 +21854,7 @@
|
||||
}
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment",
|
||||
@ -21767,13 +21903,13 @@
|
||||
}
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"description": "Integration integration",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
@ -21983,7 +22119,6 @@
|
||||
}
|
||||
},
|
||||
"receiver": {
|
||||
"description": "Receiver receiver",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"active",
|
||||
@ -22170,6 +22305,21 @@
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"GetAllIntervalsResponse": {
|
||||
"description": "(empty)",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/GettableTimeIntervals"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GetIntervalsByNameResponse": {
|
||||
"description": "(empty)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/GettableTimeIntervals"
|
||||
}
|
||||
},
|
||||
"GettableHistoricUserConfigs": {
|
||||
"description": "(empty)",
|
||||
"schema": {
|
||||
|
@ -1,6 +1,29 @@
|
||||
{
|
||||
"components": {
|
||||
"responses": {
|
||||
"GetAllIntervalsResponse": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GettableTimeIntervals"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "(empty)"
|
||||
},
|
||||
"GetIntervalsByNameResponse": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GettableTimeIntervals"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "(empty)"
|
||||
},
|
||||
"GettableHistoricUserConfigs": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
@ -6049,6 +6072,23 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"GettableTimeIntervals": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "#/components/schemas/Provenance"
|
||||
},
|
||||
"time_intervals": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TimeIntervalItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"GettableUserConfig": {
|
||||
"properties": {
|
||||
"alertmanager_config": {
|
||||
@ -8297,6 +8337,20 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"PostableTimeIntervals": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"time_intervals": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TimeIntervalItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"PostableUserConfig": {
|
||||
"properties": {
|
||||
"alertmanager_config": {
|
||||
@ -10963,6 +11017,55 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeIntervalItem": {
|
||||
"properties": {
|
||||
"days_of_month": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"months": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"times": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TimeIntervalTimeRange"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"weekdays": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"years": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeIntervalTimeRange": {
|
||||
"properties": {
|
||||
"end_time": {
|
||||
"type": "string"
|
||||
},
|
||||
"start_time": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TimeRange": {
|
||||
"description": "Redefining this to avoid an import cycle",
|
||||
"properties": {
|
||||
@ -12234,6 +12337,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"properties": {
|
||||
"comment": {
|
||||
"description": "comment",
|
||||
@ -12282,13 +12386,13 @@
|
||||
"type": "object"
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/gettableSilence"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"integration": {
|
||||
"description": "Integration integration",
|
||||
"properties": {
|
||||
"lastNotifyAttempt": {
|
||||
"description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time",
|
||||
@ -12498,7 +12602,6 @@
|
||||
"type": "object"
|
||||
},
|
||||
"receiver": {
|
||||
"description": "Receiver receiver",
|
||||
"properties": {
|
||||
"active": {
|
||||
"description": "active",
|
||||
@ -23804,6 +23907,75 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals": {
|
||||
"get": {
|
||||
"description": "Get all the time intervals",
|
||||
"operationId": "RouteNotificationsGetTimeIntervals",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/GetAllIntervalsResponse"
|
||||
},
|
||||
"403": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ForbiddenError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "ForbiddenError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/notifications/time-intervals/{name}": {
|
||||
"get": {
|
||||
"operationId": "RouteNotificationsGetTimeInterval",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Time interval name",
|
||||
"in": "path",
|
||||
"name": "name",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/GetIntervalsByNameResponse"
|
||||
},
|
||||
"403": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ForbiddenError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "ForbiddenError"
|
||||
},
|
||||
"404": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NotFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "NotFound"
|
||||
}
|
||||
},
|
||||
"summary": "Get a time interval by name.",
|
||||
"tags": [
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/provisioning/alert-rules": {
|
||||
"get": {
|
||||
"operationId": "RouteGetAlertRules",
|
||||
|
Loading…
Reference in New Issue
Block a user