feat: Updated affected components on status page incidents
This commit is contained in:
@@ -67,10 +67,66 @@ func validateIncident(inc *models.StatusIncident) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkAffectedOnPages refuses an incident naming a component none of its pages
|
||||
// carries.
|
||||
//
|
||||
// An incident's affected components are the page's own components, not the
|
||||
// fleet's monitors: publishing "api-gateway is degraded" on a page that never
|
||||
// listed api-gateway names a machine to the public that the page deliberately
|
||||
// does not, which is the same leak assembleSnapshot's redaction boundary exists
|
||||
// to prevent — reached from the authoring side instead of the read side.
|
||||
//
|
||||
// It is a separate pass rather than part of validateIncident because it reads
|
||||
// the database, and validateIncident is a pure function of the document. The
|
||||
// UI only offers the page's components, but as elsewhere the API is the
|
||||
// boundary and the UI is the courtesy.
|
||||
//
|
||||
// A monitor dropped from the page AFTER an incident named it makes the next
|
||||
// edit of that incident fail, and that is intended: the fix is one unchecked
|
||||
// box, and the alternative is a page quietly publishing a component it no
|
||||
// longer has.
|
||||
func checkAffectedOnPages(instanceID string, inc *models.StatusIncident) error {
|
||||
if len(inc.AffectedMonitors) == 0 {
|
||||
return nil
|
||||
}
|
||||
ctx, cancel := spCtx()
|
||||
defer cancel()
|
||||
cur, err := db.Col("status_pages").Find(ctx, bson.M{
|
||||
"instance_id": instanceID,
|
||||
"page_id": bson.M{"$in": inc.PageIDs},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var pages []models.StatusPage
|
||||
if err := cur.All(ctx, &pages); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
onPage := map[string]bool{}
|
||||
for _, p := range pages {
|
||||
for _, sec := range p.Sections {
|
||||
for _, e := range sec.Entries {
|
||||
onPage[e.MonitorID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, id := range inc.AffectedMonitors {
|
||||
if !onPage[id] {
|
||||
return fmt.Errorf("%w: %s is not a component of this status page; add it to the page first, or leave it out of the incident",
|
||||
ErrPageInvalid, id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateStatusIncident(instanceID string, inc *models.StatusIncident) (*models.StatusIncident, error) {
|
||||
if err := validateIncident(inc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkAffectedOnPages(instanceID, inc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inc.ID = bson.ObjectID{}
|
||||
inc.InstanceID = instanceID
|
||||
inc.IncidentID = uuid.NewString()
|
||||
@@ -172,6 +228,9 @@ func UpdateStatusIncident(instanceID, incidentID string, inc *models.StatusIncid
|
||||
if err := validateIncident(inc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkAffectedOnPages(instanceID, inc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
set := bson.M{
|
||||
"page_ids": inc.PageIDs,
|
||||
|
||||
Reference in New Issue
Block a user