forked from Mirrors/jsonapi
Add RFC3339 timestamp (#201)
@omarismail LGTM. I'm curious what you think about perhaps documenting these `iso8601` and `rfc3339` in the `Readme.md`? How did you find that this tag option/value existed? How can we make this better for others vs having to search the library implementation?
This commit is contained in:
parent
b10ff4bf78
commit
c0ee6d2554
|
@ -9,8 +9,10 @@ const (
|
|||
annotationRelation = "relation"
|
||||
annotationOmitEmpty = "omitempty"
|
||||
annotationISO8601 = "iso8601"
|
||||
annotationRFC3339 = "rfc3339"
|
||||
annotationSeperator = ","
|
||||
|
||||
rfc3339TimeFormat = "2006-01-02T15:04:05Z07:00"
|
||||
iso8601TimeFormat = "2006-01-02T15:04:05Z"
|
||||
|
||||
// MediaType is the identifier for the JSON API media type
|
||||
|
|
|
@ -31,6 +31,12 @@ type Timestamp struct {
|
|||
Next *time.Time `jsonapi:"attr,next,iso8601"`
|
||||
}
|
||||
|
||||
type TimestampRFC3339 struct {
|
||||
ID int `jsonapi:"primary,timestamps"`
|
||||
Time time.Time `jsonapi:"attr,timestamp,rfc3339"`
|
||||
Next *time.Time `jsonapi:"attr,next,rfc3339"`
|
||||
}
|
||||
|
||||
type Car struct {
|
||||
ID *string `jsonapi:"primary,cars"`
|
||||
Make *string `jsonapi:"attr,make,omitempty"`
|
||||
|
|
26
request.go
26
request.go
|
@ -23,6 +23,9 @@ var (
|
|||
// ErrInvalidISO8601 is returned when a struct has a time.Time type field and includes
|
||||
// "iso8601" in the tag spec, but the JSON value was not an ISO8601 timestamp string.
|
||||
ErrInvalidISO8601 = errors.New("Only strings can be parsed as dates, ISO8601 timestamps")
|
||||
// ErrInvalidRFC3339 is returned when a struct has a time.Time type field and includes
|
||||
// "rfc3339" in the tag spec, but the JSON value was not an RFC3339 timestamp string.
|
||||
ErrInvalidRFC3339 = errors.New("Only strings can be parsed as dates, RFC3339 timestamps")
|
||||
// ErrUnknownFieldNumberType is returned when the JSON value was a float
|
||||
// (numeric) but the Struct field was a non numeric type (i.e. not int, uint,
|
||||
// float, etc)
|
||||
|
@ -446,12 +449,15 @@ func handleStringSlice(attribute interface{}) (reflect.Value, error) {
|
|||
|
||||
func handleTime(attribute interface{}, args []string, fieldValue reflect.Value) (reflect.Value, error) {
|
||||
var isIso8601 bool
|
||||
var isRFC3339 bool
|
||||
v := reflect.ValueOf(attribute)
|
||||
|
||||
if len(args) > 2 {
|
||||
for _, arg := range args[2:] {
|
||||
if arg == annotationISO8601 {
|
||||
isIso8601 = true
|
||||
} else if arg == annotationRFC3339 {
|
||||
isRFC3339 = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -476,6 +482,26 @@ func handleTime(attribute interface{}, args []string, fieldValue reflect.Value)
|
|||
return reflect.ValueOf(t), nil
|
||||
}
|
||||
|
||||
if isRFC3339 {
|
||||
var tm string
|
||||
if v.Kind() == reflect.String {
|
||||
tm = v.Interface().(string)
|
||||
} else {
|
||||
return reflect.ValueOf(time.Now()), ErrInvalidRFC3339
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC3339, tm)
|
||||
if err != nil {
|
||||
return reflect.ValueOf(time.Now()), ErrInvalidRFC3339
|
||||
}
|
||||
|
||||
if fieldValue.Kind() == reflect.Ptr {
|
||||
return reflect.ValueOf(&t), nil
|
||||
}
|
||||
|
||||
return reflect.ValueOf(t), nil
|
||||
}
|
||||
|
||||
var at int64
|
||||
|
||||
if v.Kind() == reflect.Float64 {
|
||||
|
|
|
@ -413,6 +413,78 @@ func TestUnmarshalInvalidISO8601(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalParsesRFC3339(t *testing.T) {
|
||||
payload := &OnePayload{
|
||||
Data: &Node{
|
||||
Type: "timestamps",
|
||||
Attributes: map[string]interface{}{
|
||||
"timestamp": "2020-03-16T23:09:59+00:00",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
in := bytes.NewBuffer(nil)
|
||||
json.NewEncoder(in).Encode(payload)
|
||||
|
||||
out := new(TimestampRFC3339)
|
||||
|
||||
if err := UnmarshalPayload(in, out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := time.Date(2020, 3, 16, 23, 9, 59, 0, time.UTC)
|
||||
|
||||
if !out.Time.Equal(expected) {
|
||||
t.Fatal("Parsing the RFC3339 timestamp failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalParsesRFC3339TimePointer(t *testing.T) {
|
||||
payload := &OnePayload{
|
||||
Data: &Node{
|
||||
Type: "timestamps",
|
||||
Attributes: map[string]interface{}{
|
||||
"next": "2020-03-16T23:09:59+00:00",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
in := bytes.NewBuffer(nil)
|
||||
json.NewEncoder(in).Encode(payload)
|
||||
|
||||
out := new(TimestampRFC3339)
|
||||
|
||||
if err := UnmarshalPayload(in, out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := time.Date(2020, 3, 16, 23, 9, 59, 0, time.UTC)
|
||||
|
||||
if !out.Next.Equal(expected) {
|
||||
t.Fatal("Parsing the RFC3339 timestamp failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalInvalidRFC3339(t *testing.T) {
|
||||
payload := &OnePayload{
|
||||
Data: &Node{
|
||||
Type: "timestamps",
|
||||
Attributes: map[string]interface{}{
|
||||
"timestamp": "17 Aug 16 08:027 MST",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
in := bytes.NewBuffer(nil)
|
||||
json.NewEncoder(in).Encode(payload)
|
||||
|
||||
out := new(TimestampRFC3339)
|
||||
|
||||
if err := UnmarshalPayload(in, out); err != ErrInvalidRFC3339 {
|
||||
t.Fatalf("Expected ErrInvalidRFC3339, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalRelationshipsWithoutIncluded(t *testing.T) {
|
||||
data, err := json.Marshal(samplePayloadWithoutIncluded())
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue