Update annotations to use time as endTime
parent
bf102395aa
commit
8e542b5130
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue