bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/annotate/models.go (about) 1 package annotate 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 "time" 8 9 glob "github.com/ryanuber/go-glob" 10 ) 11 12 type RFC3339 struct { 13 time.Time 14 } 15 16 func (t RFC3339) MarshalJSON() ([]byte, error) { 17 return []byte(`"` + t.Format(time.RFC3339) + `"`), nil 18 } 19 20 func (t *RFC3339) UnmarshalJSON(b []byte) (err error) { 21 if b[0] == '"' && b[len(b)-1] == '"' { 22 b = b[1 : len(b)-1] 23 } 24 if len(b) == 0 { 25 t.Time = time.Time{} 26 return 27 } 28 t.Time, err = time.Parse(time.RFC3339, string(b)) 29 return 30 } 31 32 type Epoch struct { 33 time.Time 34 } 35 36 func (t Epoch) MarshalJSON() ([]byte, error) { 37 return []byte(fmt.Sprintf("%v", t.UTC().Unix())), nil 38 } 39 40 func (t *Epoch) UnmarshalJSON(b []byte) (err error) { 41 if len(b) == 0 { 42 t.Time = time.Time{} 43 return 44 } 45 epoch, err := strconv.ParseInt(string(b), 10, 64) 46 if err != nil { 47 return err 48 } 49 t.Time = time.Unix(epoch, 0) 50 return 51 } 52 53 func NewAnnotation(id string, start, end time.Time, user, owner, source, host, category, url, message string) (a Annotation) { 54 a.Id = id 55 a.StartDate.Time = start 56 a.EndDate.Time = end 57 a.CreationUser = user 58 a.Owner = owner 59 a.Source = source 60 a.Category = category 61 a.Url = url 62 a.Message = message 63 a.Host = host 64 return 65 } 66 67 type Annotation struct { 68 AnnotationFields 69 StartDate RFC3339 70 EndDate RFC3339 71 } 72 73 type EpochAnnotation struct { 74 AnnotationFields 75 StartDate Epoch 76 EndDate Epoch 77 } 78 79 // AnnotationsByStartID is a Type to sort by start time then by id 80 type AnnotationsByStartID Annotations 81 82 func (b AnnotationsByStartID) Len() int { return len(b) } 83 func (b AnnotationsByStartID) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 84 func (b AnnotationsByStartID) Less(i, j int) bool { 85 if b[i].StartDate.Time.Before(b[j].StartDate.Time) { 86 return true 87 } 88 if b[i].StartDate.Time.After(b[j].StartDate.Time) { 89 return false 90 } 91 return b[i].Id < b[j].Id 92 } 93 94 func emptyOrGlob(userVal, fieldVal string) bool { 95 if userVal == "empty" && fieldVal == "" { 96 return true 97 } 98 99 return glob.Glob(strings.ToLower(userVal), strings.ToLower(fieldVal)) 100 } 101 102 // Ask makes it so annotations can be filtered in memory using 103 // github.com/kylebrandt/boolq 104 func (a Annotation) Ask(filter string) (bool, error) { 105 sp := strings.SplitN(filter, ":", 2) 106 if len(sp) != 2 { 107 return false, fmt.Errorf("bad filter, filter must be in k:v format, got %v", filter) 108 } 109 key := sp[0] 110 value := sp[1] 111 switch key { 112 case "owner": 113 return emptyOrGlob(value, a.Owner), nil 114 case "user": 115 return emptyOrGlob(value, a.CreationUser), nil 116 case "host": 117 return emptyOrGlob(value, a.Host), nil 118 case "category": 119 return emptyOrGlob(value, a.Category), nil 120 case "url": 121 return emptyOrGlob(value, a.Url), nil 122 case "message": 123 return emptyOrGlob(value, a.Message), nil 124 default: 125 return false, fmt.Errorf("invalid keyword: %s", key) 126 } 127 } 128 129 func (ea *EpochAnnotation) AsAnnotation() (a Annotation) { 130 a.AnnotationFields = ea.AnnotationFields 131 a.StartDate.Time = ea.StartDate.Time 132 a.EndDate.Time = ea.EndDate.Time 133 return 134 } 135 136 func (a *Annotation) AsEpochAnnotation() (ea EpochAnnotation) { 137 ea.AnnotationFields = a.AnnotationFields 138 ea.StartDate.Time = a.StartDate.Time 139 ea.EndDate.Time = a.EndDate.Time 140 return 141 } 142 143 type AnnotationFields struct { 144 Id string 145 Message string 146 CreationUser string 147 Url string 148 Source string 149 Host string 150 Owner string 151 Category string 152 } 153 154 const ( 155 Message = "Message" 156 StartDate = "StartDate" 157 EndDate = "EndDate" 158 Source = "Source" 159 Host = "Host" 160 CreationUser = "CreationUser" 161 Owner = "Owner" 162 Category = "Category" 163 Url = "Url" 164 ) 165 166 type Annotations []Annotation 167 type EpochAnnotations []EpochAnnotation 168 169 func (as Annotations) AsEpochAnnotations() EpochAnnotations { 170 eas := make(EpochAnnotations, len(as)) 171 for i, a := range as { 172 eas[i] = a.AsEpochAnnotation() 173 } 174 return eas 175 } 176 177 func (a *Annotation) SetNow() { 178 a.StartDate.Time = time.Now() 179 a.EndDate = a.StartDate 180 } 181 182 func (a *Annotation) IsTimeNotSet() bool { 183 t := time.Time{} 184 return a.StartDate.Equal(t) || a.EndDate.Equal(t) 185 } 186 187 func (a *Annotation) IsOneTimeSet() bool { 188 t := time.Time{} 189 return (a.StartDate.Equal(t) && !a.EndDate.Equal(t)) || (!a.StartDate.Equal(t) && a.EndDate.Equal(t)) 190 } 191 192 // Match Times Sets Both times to the greater of the two times 193 func (a *Annotation) MatchTimes() { 194 if a.StartDate.After(a.EndDate.Time) { 195 a.EndDate = a.StartDate 196 return 197 } 198 a.StartDate = a.EndDate 199 } 200 201 func (a *Annotation) ValidateTime() error { 202 t := time.Time{} 203 if a.StartDate.Equal(t) { 204 return fmt.Errorf("StartDate is not set") 205 } 206 if a.EndDate.Equal(t) { 207 return fmt.Errorf("StartDate is not set") 208 } 209 if a.EndDate.Before(a.StartDate.Time) { 210 return fmt.Errorf("EndDate is before StartDate") 211 } 212 return nil 213 }