From 2a23014b3b3930e59e4f141a5e46dc18a1a51915 Mon Sep 17 00:00:00 2001 From: Nick Pillitteri Date: Fri, 5 Mar 2021 10:19:20 -0500 Subject: [PATCH 1/2] Add methods to model.Duration for JSON marshalling and unmarshalling Add methods to model.Duration structs to allow them to be serialized and unserialized to/from JSON. This helps downstream consumers of the structs that attempt to use them in objects that are expected to be able to be represented as JSON (such as Cortex). Signed-off-by: Nick Pillitteri --- model/time.go | 17 ++++++++++ model/time_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/model/time.go b/model/time.go index 77b82b2b..871e78c7 100644 --- a/model/time.go +++ b/model/time.go @@ -14,6 +14,7 @@ package model import ( + "encoding/json" "fmt" "math" "regexp" @@ -254,6 +255,22 @@ func (d Duration) String() string { return r } +// MarshalJSON implements the json.Marshaler interface. +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(d.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *Duration) UnmarshalJSON(bytes []byte) error { + var err error + var s string + if err := json.Unmarshal(bytes, &s); err != nil { + return err + } + *d, err = ParseDuration(s) + return err +} + // MarshalText implements the encoding.TextMarshaler interface. func (d *Duration) MarshalText() ([]byte, error) { return []byte(d.String()), nil diff --git a/model/time_test.go b/model/time_test.go index 14fb9eb9..a65b859d 100644 --- a/model/time_test.go +++ b/model/time_test.go @@ -14,6 +14,7 @@ package model import ( + "encoding/json" "strconv" "testing" "time" @@ -230,6 +231,82 @@ func TestDuration_UnmarshalText(t *testing.T) { } } +func TestDuration_UnmarshalJSON(t *testing.T) { + var cases = []struct { + in string + out time.Duration + + expectedString string + }{ + { + in: `"0"`, + out: 0, + expectedString: `"0s"`, + }, { + in: `"0w"`, + out: 0, + expectedString: `"0s"`, + }, { + in: `"0s"`, + out: 0, + }, { + in: `"324ms"`, + out: 324 * time.Millisecond, + }, { + in: `"3s"`, + out: 3 * time.Second, + }, { + in: `"5m"`, + out: 5 * time.Minute, + }, { + in: `"1h"`, + out: time.Hour, + }, { + in: `"4d"`, + out: 4 * 24 * time.Hour, + }, { + in: `"4d1h"`, + out: 4*24*time.Hour + time.Hour, + }, { + in: `"14d"`, + out: 14 * 24 * time.Hour, + expectedString: `"2w"`, + }, { + in: `"3w"`, + out: 3 * 7 * 24 * time.Hour, + }, { + in: `"3w2d1h"`, + out: 3*7*24*time.Hour + 2*24*time.Hour + time.Hour, + expectedString: `"23d1h"`, + }, { + in: `"10y"`, + out: 10 * 365 * 24 * time.Hour, + }, + } + + for _, c := range cases { + var d Duration + err := json.Unmarshal([]byte(c.in), &d) + if err != nil { + t.Errorf("Unexpected error on input %q", c.in) + } + if time.Duration(d) != c.out { + t.Errorf("Expected %v but got %v", c.out, d) + } + expectedString := c.expectedString + if c.expectedString == "" { + expectedString = c.in + } + bytes, err := json.Marshal(d) + if err != nil { + t.Errorf("Unexpected error on marshal of %v: %s", d, err) + } + if string(bytes) != expectedString { + t.Errorf("Expected duration string %q but got %q", c.in, d.String()) + } + } +} + func TestParseBadDuration(t *testing.T) { var cases = []string{ "1", From 745afa4fc73839c5da259b932cfcedae1406c79f Mon Sep 17 00:00:00 2001 From: Nick Pillitteri Date: Fri, 5 Mar 2021 14:56:18 -0500 Subject: [PATCH 2/2] Code review feedback Signed-off-by: Nick Pillitteri --- model/time.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/model/time.go b/model/time.go index 871e78c7..e684ebd1 100644 --- a/model/time.go +++ b/model/time.go @@ -262,13 +262,16 @@ func (d Duration) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the json.Unmarshaler interface. func (d *Duration) UnmarshalJSON(bytes []byte) error { - var err error var s string if err := json.Unmarshal(bytes, &s); err != nil { return err } - *d, err = ParseDuration(s) - return err + dur, err := ParseDuration(s) + if err != nil { + return err + } + *d = dur + return nil } // MarshalText implements the encoding.TextMarshaler interface.