Update annotations to use time as endTime

pull/10616/head
Chris Goller 2018-01-25 14:36:28 -08:00 committed by Luke Morris
parent 11121e1ac5
commit 5731d3f059
3 changed files with 57 additions and 78 deletions

View File

@ -487,11 +487,11 @@ type Databases interface {
// Annotation represents a time-based metadata associated with a source
type Annotation struct {
ID string // ID is the unique annotation identifier
Time time.Time // Time is the start time of the annotation
Duration time.Duration // Duration of the annotation
Text string // Text is the associated user-facing text describing the annotation
Type string // Type describes the kind of annotation
ID string // ID is the unique annotation identifier
StartTime time.Time // StartTime starts the annotation
EndTime time.Time // EndTime ends the annotation
Text string // Text is the associated user-facing text describing the annotation
Type string // Type describes the kind of annotation
}
// AnnotationStore represents storage and retrieval of annotations

View File

@ -14,9 +14,9 @@ import (
const (
// AllAnnotations returns all annotations from the chronograf database
AllAnnotations = `SELECT "duration_ns", "modified_time_ns", "text", "type", "id" FROM "chronograf"."autogen"."annotations" WHERE "deleted"=false AND time > %dns and time < %dns ORDER BY time DESC`
AllAnnotations = `SELECT "start_time", "modified_time_ns", "text", "type", "id" FROM "chronograf"."autogen"."annotations" WHERE "deleted"=false AND time > %dns and start_time < %dns ORDER BY time DESC`
// GetAnnotationID returns all annotations from the chronograf database where id is %s
GetAnnotationID = `SELECT "duration_ns", "modified_time_ns", "text", "type", "id" FROM "chronograf"."autogen"."annotations" WHERE "id"='%s' AND "deleted"=false ORDER BY time DESC`
GetAnnotationID = `SELECT "start_time", "modified_time_ns", "text", "type", "id" FROM "chronograf"."autogen"."annotations" WHERE "id"='%s' AND "deleted"=false ORDER BY time DESC`
// DefaultDB is chronograf. Perhaps later we allow this to be changed
DefaultDB = "chronograf"
// DefaultRP is autogen. Perhaps later we allow this to be changed
@ -91,7 +91,7 @@ func (a *AnnotationStore) Update(ctx context.Context, anno *chronograf.Annotatio
// If the updated annotation has a different time, then, we must
// delete the previous annotation
if cur.Time != anno.Time {
if cur.EndTime != anno.EndTime {
return a.client.Write(ctx, toDeletedPoint(cur))
}
return nil
@ -126,13 +126,13 @@ func toPoint(anno *chronograf.Annotation) *chronograf.Point {
Database: DefaultDB,
RetentionPolicy: DefaultRP,
Measurement: DefaultMeasurement,
Time: anno.Time.UnixNano(),
Time: anno.EndTime.UnixNano(),
Tags: map[string]string{
"id": anno.ID,
},
Fields: map[string]interface{}{
"deleted": false,
"duration_ns": int64(anno.Duration),
"start_time": anno.StartTime.UnixNano(),
"modified_time_ns": int64(time.Now().UnixNano()),
"text": anno.Text,
"type": anno.Type,
@ -145,13 +145,13 @@ func toDeletedPoint(anno *chronograf.Annotation) *chronograf.Point {
Database: DefaultDB,
RetentionPolicy: DefaultRP,
Measurement: DefaultMeasurement,
Time: anno.Time.UnixNano(),
Time: anno.EndTime.UnixNano(),
Tags: map[string]string{
"id": anno.ID,
},
Fields: map[string]interface{}{
"deleted": true,
"duration_ns": 0,
"start_time": int64(0),
"modified_time_ns": int64(time.Now().UnixNano()),
"text": "",
"type": "",
@ -180,14 +180,6 @@ func (v value) Time(idx int) (time.Time, error) {
return time.Unix(0, tm), nil
}
func (v value) Duration(idx int) (time.Duration, error) {
dur, err := v.Int64(idx)
if err != nil {
return 0, err
}
return time.Duration(dur), nil
}
func (v value) String(idx int) (string, error) {
if idx >= len(v) {
return "", fmt.Errorf("index %d does not exist in values", idx)
@ -222,11 +214,11 @@ func (r *influxResults) Annotations() (res []chronograf.Annotation, err error) {
for _, v := range s.Values {
anno := annotationResult{}
if anno.Time, err = v.Time(0); err != nil {
if anno.EndTime, err = v.Time(0); err != nil {
return
}
if anno.Duration, err = v.Duration(1); err != nil {
if anno.StartTime, err = v.Time(1); err != nil {
return
}

View File

@ -5,7 +5,6 @@ import (
"fmt"
"net/http"
"net/url"
"strconv"
"time"
"github.com/influxdata/chronograf"
@ -23,22 +22,22 @@ type annotationLinks struct {
}
type annotationResponse struct {
ID string `json:"id"` // ID is the unique annotation identifier
Time string `json:"time"` // Time in RFC3339 of the start of the annotation
Duration string `json:"duration"` // Duration is the duration in milliseconds of the annotation
Text string `json:"text"` // Text is the associated user-facing text describing the annotation
Type string `json:"type"` // Type describes the kind of annotation
Links annotationLinks `json:"links"`
ID string `json:"id"` // ID is the unique annotation identifier
StartTime string `json:"startTime"` // StartTime in RFC3339 of the start of the annotation
EndTime string `json:"endTime"` // EndTime in RFC3339 of the end of the annotation
Text string `json:"text"` // Text is the associated user-facing text describing the annotation
Type string `json:"type"` // Type describes the kind of annotation
Links annotationLinks `json:"links"`
}
func newAnnotationResponse(src chronograf.Source, a *chronograf.Annotation) annotationResponse {
base := "/chronograf/v1/sources"
return annotationResponse{
ID: a.ID,
Time: a.Time.Format(timeMilliFormat),
Duration: fmt.Sprintf("%d", int64(a.Duration/time.Millisecond)),
Text: a.Text,
Type: a.Type,
ID: a.ID,
StartTime: a.StartTime.Format(timeMilliFormat),
EndTime: a.EndTime.Format(timeMilliFormat),
Text: a.Text,
Type: a.Type,
Links: annotationLinks{
Self: fmt.Sprintf("%s/%d/annotations/%s", base, src.ID, a.ID),
},
@ -178,17 +177,17 @@ func (s *Service) Annotation(w http.ResponseWriter, r *http.Request) {
}
type newAnnotationRequest struct {
Time time.Time `json:"time"` // Time is the time in rfc3339 milliseconds
Duration time.Duration `json:"duration"` // Duration is the annotation duration in milliseconds
Text string `json:"text,omitempty"` // Text is the associated user-facing text describing the annotation
Type string `json:"type,omitempty"` // Type describes the kind of annotation
StartTime time.Time `json:"startTime"` // StartTime is the time in rfc3339 milliseconds
EndTime time.Time `json:"endTime"` // EndTime is the time in rfc3339 milliseconds
Text string `json:"text,omitempty"` // Text is the associated user-facing text describing the annotation
Type string `json:"type,omitempty"` // Type describes the kind of annotation
}
func (ar *newAnnotationRequest) UnmarshalJSON(data []byte) error {
type Alias newAnnotationRequest
aux := &struct {
Time string `json:"time"`
Duration string `json:"duration"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
*Alias
}{
Alias: (*Alias)(ar),
@ -198,21 +197,14 @@ func (ar *newAnnotationRequest) UnmarshalJSON(data []byte) error {
}
var err error
ar.Time, err = time.Parse(timeMilliFormat, aux.Time)
ar.StartTime, err = time.Parse(timeMilliFormat, aux.StartTime)
if err != nil {
return err
}
if aux.Duration != "" {
// duration in milliseconds is a max of 13 characters
if len(aux.Duration) > 13 {
return fmt.Errorf("duration must be in milliseconds since unix epoch")
}
d, err := strconv.ParseInt(aux.Duration, 10, 64)
if err != nil {
return err
}
ar.Duration = time.Duration(d) * time.Millisecond
ar.EndTime, err = time.Parse(timeMilliFormat, aux.EndTime)
if err != nil {
return err
}
return nil
@ -220,10 +212,10 @@ func (ar *newAnnotationRequest) UnmarshalJSON(data []byte) error {
func (ar *newAnnotationRequest) Annotation() *chronograf.Annotation {
return &chronograf.Annotation{
Time: ar.Time,
Duration: ar.Duration,
Text: ar.Text,
Type: ar.Type,
StartTime: ar.StartTime,
EndTime: ar.EndTime,
Text: ar.Text,
Type: ar.Type,
}
}
@ -327,17 +319,17 @@ func (s *Service) RemoveAnnotation(w http.ResponseWriter, r *http.Request) {
}
type updateAnnotationRequest struct {
Time *time.Time `json:"time,omitempty"` // Time is the time in rfc3339 milliseconds
Duration *time.Duration `json:"duration,omitempty"` // Duration is the annotation duration in milliseconds
Text *string `json:"text,omitempty"` // Text is the associated user-facing text describing the annotation
Type *string `json:"type,omitempty"` // Type describes the kind of annotation
StartTime *time.Time `json:"startTime,omitempty"` // StartTime is the time in rfc3339 milliseconds
EndTime *time.Time `json:"endTime,omitempty"` // EndTime is the time in rfc3339 milliseconds
Text *string `json:"text,omitempty"` // Text is the associated user-facing text describing the annotation
Type *string `json:"type,omitempty"` // Type describes the kind of annotation
}
func (u *updateAnnotationRequest) UnmarshalJSON(data []byte) error {
type Alias updateAnnotationRequest
aux := &struct {
Time *string `json:"time,omitempty"`
Duration *string `json:"duration,omitempty"`
StartTime *string `json:"startTime,omitempty"`
EndTime *string `json:"endTime,omitempty"`
*Alias
}{
Alias: (*Alias)(u),
@ -346,29 +338,24 @@ func (u *updateAnnotationRequest) UnmarshalJSON(data []byte) error {
return err
}
if aux.Time != nil {
tm, err := time.Parse(timeMilliFormat, *aux.Time)
if aux.StartTime != nil {
tm, err := time.Parse(timeMilliFormat, *aux.StartTime)
if err != nil {
return err
}
u.Time = &tm
u.StartTime = &tm
}
if aux.Duration != nil {
// duration in milliseconds is a max of 13 characters
if len(*aux.Duration) > 13 {
return fmt.Errorf("duration must be in milliseconds since unix epoch")
}
d, err := strconv.ParseInt(*aux.Duration, 10, 64)
if aux.EndTime != nil {
tm, err := time.Parse(timeMilliFormat, *aux.EndTime)
if err != nil {
return err
}
dur := time.Duration(d) * time.Millisecond
u.Duration = &dur
u.EndTime = &tm
}
// Update must have at least one field set
if u.Time == nil && u.Duration == nil && u.Text == nil && u.Type == nil {
if u.StartTime == nil && u.EndTime == nil && u.Text == nil && u.Type == nil {
return fmt.Errorf("update request must have at least one field")
}
@ -422,15 +409,15 @@ func (s *Service) UpdateAnnotation(w http.ResponseWriter, r *http.Request) {
return
}
if req.Duration != nil {
cur.Duration = *req.Duration
if req.StartTime != nil {
cur.StartTime = *req.StartTime
}
if req.EndTime != nil {
cur.EndTime = *req.EndTime
}
if req.Text != nil {
cur.Text = *req.Text
}
if req.Time != nil {
cur.Time = *req.Time
}
if req.Type != nil {
cur.Type = *req.Type
}