Update annotations to use time as endTime

pull/2825/head
Chris Goller 2018-01-25 14:36:28 -08:00 committed by Luke Morris
parent bf102395aa
commit 8e542b5130
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 // Annotation represents a time-based metadata associated with a source
type Annotation struct { type Annotation struct {
ID string // ID is the unique annotation identifier ID string // ID is the unique annotation identifier
Time time.Time // Time is the start time of the annotation StartTime time.Time // StartTime starts the annotation
Duration time.Duration // Duration of the annotation EndTime time.Time // EndTime ends the annotation
Text string // Text is the associated user-facing text describing the annotation Text string // Text is the associated user-facing text describing the annotation
Type string // Type describes the kind of annotation Type string // Type describes the kind of annotation
} }
// AnnotationStore represents storage and retrieval of annotations // AnnotationStore represents storage and retrieval of annotations

View File

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

View File

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