Skip to content

Commit

Permalink
lift Repeat out of the base alert config
Browse files Browse the repository at this point in the history
Through discussions it was confirmed that Repeat is not universal to all
alerts. So it's lifted out of the Base alert and re-inserted into those alerts
where it should be present (namely Low and High alerts only).

BACK-2554
  • Loading branch information
ewollesen committed Jun 24, 2024
1 parent 8367902 commit 13aec20
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 62 deletions.
28 changes: 17 additions & 11 deletions alerts/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,10 @@ func (a Alerts) Validate(validator structure.Validator) {
type Base struct {
// Enabled controls whether notifications should be sent for this alert.
Enabled bool `json:"enabled" bson:"enabled"`
// Repeat is measured in minutes.
//
// A value of 0 (the default) disables repeat notifications.
Repeat DurationMinutes `json:"repeat,omitempty" bson:"repeat"`
}

func (b Base) Validate(validator structure.Validator) {
validator.Bool("enabled", &b.Enabled)
dur := b.Repeat.Duration()
validator.Duration("repeat", &dur).Using(validateRepeat)
}

const (
Expand Down Expand Up @@ -110,7 +104,7 @@ type UrgentLowAlert struct {
Base `bson:",inline"`
// Threshold is compared the current value to determine if an alert should
// be triggered.
Threshold `json:"threshold"`
Threshold `json:"threshold" bson:"threshold"`
}

func (a UrgentLowAlert) Validate(validator structure.Validator) {
Expand Down Expand Up @@ -149,13 +143,19 @@ type LowAlert struct {
// be triggered.
Threshold `json:"threshold"`
Delay DurationMinutes `json:"delay,omitempty"`
// Repeat is measured in minutes.
//
// A value of 0 (the default) disables repeat notifications.
Repeat DurationMinutes `json:"repeat,omitempty" bson:"repeat"`
}

func (a LowAlert) Validate(validator structure.Validator) {
a.Base.Validate(validator)
dur := a.Delay.Duration()
validator.Duration("delay", &dur).InRange(0, 2*time.Hour)
delayDur := a.Delay.Duration()
validator.Duration("delay", &delayDur).InRange(0, 2*time.Hour)
a.Threshold.Validate(validator)
repeatDur := a.Repeat.Duration()
validator.Duration("repeat", &repeatDur).Using(validateRepeat)
}

// HighAlert extends Base with a threshold and a delay.
Expand All @@ -165,13 +165,19 @@ type HighAlert struct {
// be triggered.
Threshold `json:"threshold"`
Delay DurationMinutes `json:"delay,omitempty"`
// Repeat is measured in minutes.
//
// A value of 0 (the default) disables repeat notifications.
Repeat DurationMinutes `json:"repeat,omitempty" bson:"repeat"`
}

func (a HighAlert) Validate(validator structure.Validator) {
a.Base.Validate(validator)
a.Threshold.Validate(validator)
dur := a.Delay.Duration()
validator.Duration("delay", &dur).InRange(0, 6*time.Hour)
delayDur := a.Delay.Duration()
validator.Duration("delay", &delayDur).InRange(0, 6*time.Hour)
repeatDur := a.Repeat.Duration()
validator.Duration("repeat", &repeatDur).Using(validateRepeat)
}

// DurationMinutes reads a JSON integer and converts it to a time.Duration.
Expand Down
106 changes: 55 additions & 51 deletions alerts/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ var _ = Describe("Config", func() {
},
"urgentLow": {
"enabled": false,
"repeat": 30,
"threshold": {
"units": "mg/dL",
"value": 47.5
Expand All @@ -60,12 +59,10 @@ var _ = Describe("Config", func() {
},
"notLooping": {
"enabled": true,
"repeat": 32,
"delay": 4
},
"noCommunication": {
"enabled": true,
"repeat": 33,
"delay": 6
}
}`, mockUserID1, mockUserID2, mockUploadID)
Expand All @@ -86,14 +83,11 @@ var _ = Describe("Config", func() {
Expect(conf.Low.Threshold.Value).To(Equal(80.0))
Expect(conf.Low.Threshold.Units).To(Equal(glucose.MgdL))
Expect(conf.UrgentLow.Enabled).To(Equal(false))
Expect(conf.UrgentLow.Repeat).To(Equal(DurationMinutes(30 * time.Minute)))
Expect(conf.UrgentLow.Threshold.Value).To(Equal(47.5))
Expect(conf.UrgentLow.Threshold.Units).To(Equal(glucose.MgdL))
Expect(conf.NotLooping.Enabled).To(Equal(true))
Expect(conf.NotLooping.Repeat).To(Equal(DurationMinutes(32 * time.Minute)))
Expect(conf.NotLooping.Delay).To(Equal(DurationMinutes(4 * time.Minute)))
Expect(conf.NoCommunication.Enabled).To(Equal(true))
Expect(conf.NoCommunication.Repeat).To(Equal(DurationMinutes(33 * time.Minute)))
Expect(conf.NoCommunication.Delay).To(Equal(DurationMinutes(6 * time.Minute)))
})

Expand Down Expand Up @@ -322,32 +316,41 @@ var _ = Describe("Config", func() {
})

Context("repeat", func() {
var defaultAlert = LowAlert{
Threshold: Threshold{Value: 11, Units: glucose.MmolL},
}

It("accepts values of 0 (indicating disabled)", func() {
val := validator.New()
b := Base{Repeat: 0}
b.Validate(val)
l := defaultAlert
l.Repeat = 0
l.Validate(val)
Expect(val.Error()).To(Succeed())
})

It("accepts values of 15 minutes to 4 hours (inclusive)", func() {
val := validator.New()
b := Base{Repeat: DurationMinutes(15 * time.Minute)}
b.Validate(val)
l := defaultAlert
l.Repeat = DurationMinutes(15 * time.Minute)
l.Validate(val)
Expect(val.Error()).To(Succeed())

val = validator.New()
b = Base{Repeat: DurationMinutes(4 * time.Hour)}
b.Validate(val)
l = defaultAlert
l.Repeat = DurationMinutes(4 * time.Hour)
l.Validate(val)
Expect(val.Error()).To(Succeed())

val = validator.New()
b = Base{Repeat: DurationMinutes(4*time.Hour + 1)}
b.Validate(val)
l = defaultAlert
l.Repeat = DurationMinutes(4*time.Hour + 1)
l.Validate(val)
Expect(val.Error()).NotTo(Succeed())

val = validator.New()
b = Base{Repeat: DurationMinutes(15*time.Minute - 1)}
b.Validate(val)
l = defaultAlert
l.Repeat = DurationMinutes(15*time.Minute - 1)
l.Validate(val)
Expect(val.Error()).NotTo(Succeed())
})
})
Expand All @@ -359,67 +362,68 @@ var _ = Describe("Config", func() {
err := request.DecodeObject(nil, buf, threshold)
Expect(err).To(MatchError("json is malformed"))
})
It("validates repeat minutes (negative)", func() {
})

Context("low", func() {
It("accepts a blank repeat", func() {
buf := buff(`{
"userId": "%s",
"followedUserId": "%s",
"uploadId": "%s",
"urgentLow": {
"enabled": false,
"repeat": -11,
"low": {
"enabled": true,
"delay": 10,
"threshold": {
"units": "%s",
"value": 47.5
"units": "mg/dL",
"value": 80
}
}
}`, mockUserID1, mockUserID2, mockUploadID, glucose.MgdL)
cfg := &Config{}
err := request.DecodeObject(nil, buf, cfg)
Expect(err).To(MatchError("value -11m0s is not greater than or equal to 15m0s"))
}`, mockUserID1, mockUserID2, mockUploadID)
conf := &Config{}
err := request.DecodeObject(nil, buf, conf)
Expect(err).To(Succeed())
Expect(conf.Low.Repeat).To(Equal(DurationMinutes(0)))
})
It("validates repeat minutes (string)", func() {
buf := buff(`{
})
It("validates repeat minutes (negative)", func() {
buf := buff(`{
"userId": "%s",
"followedUserId": "%s",
"urgentLow": {
"uploadId": "%s",
"low": {
"enabled": false,
"repeat": "a",
"repeat": -11,
"threshold": {
"units": "%s",
"value": 1
"value": 47.5
}
}
}`, mockUserID1, mockUserID2, glucose.MgdL)
cfg := &Config{}
err := request.DecodeObject(nil, buf, cfg)
Expect(err).To(MatchError("json is malformed"))
})
}`, mockUserID1, mockUserID2, mockUploadID, glucose.MgdL)
cfg := &Config{}
err := request.DecodeObject(nil, buf, cfg)
Expect(err).To(MatchError("value -11m0s is not greater than or equal to 15m0s"))
})

Context("low", func() {
It("accepts a blank repeat", func() {
buf := buff(`{
It("validates repeat minutes (string)", func() {
buf := buff(`{
"userId": "%s",
"followedUserId": "%s",
"uploadId": "%s",
"low": {
"enabled": true,
"delay": 10,
"enabled": false,
"repeat": "a",
"threshold": {
"units": "mg/dL",
"value": 80
"units": "%s",
"value": 1
}
}
}`, mockUserID1, mockUserID2, mockUploadID)
conf := &Config{}
err := request.DecodeObject(nil, buf, conf)
Expect(err).To(Succeed())
Expect(conf.Low.Repeat).To(Equal(DurationMinutes(0)))
})
}`, mockUserID1, mockUserID2, mockUploadID, glucose.MgdL)
cfg := &Config{}
err := request.DecodeObject(nil, buf, cfg)
Expect(err).To(MatchError("json is malformed"))
})
})

var _ = Describe("Duration", func() {
var _ = Describe("DurationMinutes", func() {
It("parses 42", func() {
d := DurationMinutes(0)
err := d.UnmarshalJSON([]byte(`42`))
Expand Down

0 comments on commit 13aec20

Please sign in to comment.