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  }