mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Add a time type for use in APIs. (#9911)
* Add a time type for use in APIs. * go mod vendor
This commit is contained in:
@@ -625,6 +625,8 @@ func (t FieldType) Zero() interface{} {
|
||||
return http.Header{}
|
||||
case TypeFloat:
|
||||
return 0.0
|
||||
case TypeTime:
|
||||
return time.Time{}
|
||||
default:
|
||||
panic("unknown type: " + t.String())
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (d *FieldData) Validate() error {
|
||||
switch schema.Type {
|
||||
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
||||
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat:
|
||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat, TypeTime:
|
||||
_, _, err := d.getPrimitive(field, schema)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("error converting input %v for field %q: {{err}}", value, field), err)
|
||||
@@ -133,7 +133,7 @@ func (d *FieldData) GetOkErr(k string) (interface{}, bool, error) {
|
||||
switch schema.Type {
|
||||
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
||||
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat:
|
||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat, TypeTime:
|
||||
return d.getPrimitive(k, schema)
|
||||
default:
|
||||
return nil, false,
|
||||
@@ -221,6 +221,19 @@ func (d *FieldData) getPrimitive(k string, schema *FieldSchema) (interface{}, bo
|
||||
}
|
||||
return result, true, nil
|
||||
|
||||
case TypeTime:
|
||||
switch inp := raw.(type) {
|
||||
case nil:
|
||||
// Handle nil interface{} as a non-error case
|
||||
return nil, false, nil
|
||||
default:
|
||||
time, err := parseutil.ParseAbsoluteTime(inp)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return time, true, nil
|
||||
}
|
||||
|
||||
case TypeCommaIntSlice:
|
||||
var result []int
|
||||
config := &mapstructure.DecoderConfig{
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFieldDataGet(t *testing.T) {
|
||||
@@ -931,6 +932,40 @@ func TestFieldDataGet(t *testing.T) {
|
||||
0.0,
|
||||
true,
|
||||
},
|
||||
|
||||
"type time, not supplied": {
|
||||
map[string]*FieldSchema{
|
||||
"foo": {Type: TypeTime},
|
||||
},
|
||||
map[string]interface{}{},
|
||||
"foo",
|
||||
time.Time{},
|
||||
false,
|
||||
},
|
||||
"type time, string value": {
|
||||
map[string]*FieldSchema{
|
||||
"foo": {Type: TypeTime},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"foo": "2021-12-11T09:08:07Z",
|
||||
},
|
||||
"foo",
|
||||
// Comparison uses DeepEqual() so better match exactly,
|
||||
// can't have a different location.
|
||||
time.Date(2021, 12, 11, 9, 8, 7, 0, time.UTC),
|
||||
false,
|
||||
},
|
||||
"type time, invalid value": {
|
||||
map[string]*FieldSchema{
|
||||
"foo": {Type: TypeTime},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"foo": "2021-13-11T09:08:07+02:00",
|
||||
},
|
||||
"foo",
|
||||
time.Time{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
|
||||
@@ -56,6 +56,9 @@ const (
|
||||
|
||||
// TypeFloat parses both float32 and float64 values
|
||||
TypeFloat
|
||||
|
||||
// TypeTime represents absolute time, using an RFC3999 format on the wire
|
||||
TypeTime
|
||||
)
|
||||
|
||||
func (t FieldType) String() string {
|
||||
@@ -82,6 +85,8 @@ func (t FieldType) String() string {
|
||||
return "header"
|
||||
case TypeFloat:
|
||||
return "float"
|
||||
case TypeTime:
|
||||
return "time"
|
||||
default:
|
||||
return "unknown type"
|
||||
}
|
||||
|
||||
@@ -67,6 +67,54 @@ func ParseDurationSecond(in interface{}) (time.Duration, error) {
|
||||
return dur, nil
|
||||
}
|
||||
|
||||
func ParseAbsoluteTime(in interface{}) (time.Time, error) {
|
||||
var t time.Time
|
||||
switch inp := in.(type) {
|
||||
case nil:
|
||||
// return default of zero
|
||||
return t, nil
|
||||
case string:
|
||||
// Allow RFC3339 with nanoseconds, or without,
|
||||
// or an epoch time as an integer.
|
||||
var err error
|
||||
t, err = time.Parse(time.RFC3339Nano, inp)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
t, err = time.Parse(time.RFC3339, inp)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
epochTime, err := strconv.ParseInt(inp, 10, 64)
|
||||
if err == nil {
|
||||
t = time.Unix(epochTime, 0)
|
||||
break
|
||||
}
|
||||
return t, errors.New("could not parse string as date and time")
|
||||
case json.Number:
|
||||
epochTime, err := inp.Int64()
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
t = time.Unix(epochTime, 0)
|
||||
case int:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
case int32:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
case int64:
|
||||
t = time.Unix(inp, 0)
|
||||
case uint:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
case uint32:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
case uint64:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
default:
|
||||
return t, errors.New("could not parse time from input type")
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func ParseInt(in interface{}) (int64, error) {
|
||||
var ret int64
|
||||
jsonIn, ok := in.(json.Number)
|
||||
|
||||
@@ -30,6 +30,86 @@ func Test_ParseDurationSecond(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ParseAbsoluteTime(t *testing.T) {
|
||||
testCases := []struct {
|
||||
inp interface{}
|
||||
valid bool
|
||||
expected time.Time
|
||||
}{
|
||||
{
|
||||
"2020-12-11T09:08:07.654321Z",
|
||||
true,
|
||||
time.Date(2020, 12, 11, 9, 8, 7, 654321000, time.UTC),
|
||||
},
|
||||
{
|
||||
"2020-12-11T09:08:07+02:00",
|
||||
true,
|
||||
time.Date(2020, 12, 11, 7, 8, 7, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
"2021-12-11T09:08:07Z",
|
||||
true,
|
||||
time.Date(2021, 12, 11, 9, 8, 7, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
"2021-12-11T09:08:07",
|
||||
false,
|
||||
time.Time{},
|
||||
},
|
||||
{
|
||||
"1670749687",
|
||||
true,
|
||||
time.Date(2022, 12, 11, 9, 8, 7, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
1670749687,
|
||||
true,
|
||||
time.Date(2022, 12, 11, 9, 8, 7, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
uint32(1670749687),
|
||||
true,
|
||||
time.Date(2022, 12, 11, 9, 8, 7, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
json.Number("1670749687"),
|
||||
true,
|
||||
time.Date(2022, 12, 11, 9, 8, 7, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
nil,
|
||||
true,
|
||||
time.Time{},
|
||||
},
|
||||
{
|
||||
struct{}{},
|
||||
false,
|
||||
time.Time{},
|
||||
},
|
||||
{
|
||||
true,
|
||||
false,
|
||||
time.Time{},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
outp, err := ParseAbsoluteTime(tc.inp)
|
||||
if err != nil {
|
||||
if tc.valid {
|
||||
t.Errorf("failed to parse: %v", tc.inp)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err == nil && !tc.valid {
|
||||
t.Errorf("no error for: %v", tc.inp)
|
||||
continue
|
||||
}
|
||||
if !outp.Equal(tc.expected) {
|
||||
t.Errorf("input %v parsed as %v, expected %v", tc.inp, outp, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ParseBool(t *testing.T) {
|
||||
outp, err := ParseBool("true")
|
||||
if err != nil {
|
||||
|
||||
2
vendor/github.com/hashicorp/vault/sdk/framework/backend.go
generated
vendored
2
vendor/github.com/hashicorp/vault/sdk/framework/backend.go
generated
vendored
@@ -625,6 +625,8 @@ func (t FieldType) Zero() interface{} {
|
||||
return http.Header{}
|
||||
case TypeFloat:
|
||||
return 0.0
|
||||
case TypeTime:
|
||||
return time.Time{}
|
||||
default:
|
||||
panic("unknown type: " + t.String())
|
||||
}
|
||||
|
||||
17
vendor/github.com/hashicorp/vault/sdk/framework/field_data.go
generated
vendored
17
vendor/github.com/hashicorp/vault/sdk/framework/field_data.go
generated
vendored
@@ -40,7 +40,7 @@ func (d *FieldData) Validate() error {
|
||||
switch schema.Type {
|
||||
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
||||
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat:
|
||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat, TypeTime:
|
||||
_, _, err := d.getPrimitive(field, schema)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("error converting input %v for field %q: {{err}}", value, field), err)
|
||||
@@ -133,7 +133,7 @@ func (d *FieldData) GetOkErr(k string) (interface{}, bool, error) {
|
||||
switch schema.Type {
|
||||
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
||||
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat:
|
||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat, TypeTime:
|
||||
return d.getPrimitive(k, schema)
|
||||
default:
|
||||
return nil, false,
|
||||
@@ -221,6 +221,19 @@ func (d *FieldData) getPrimitive(k string, schema *FieldSchema) (interface{}, bo
|
||||
}
|
||||
return result, true, nil
|
||||
|
||||
case TypeTime:
|
||||
switch inp := raw.(type) {
|
||||
case nil:
|
||||
// Handle nil interface{} as a non-error case
|
||||
return nil, false, nil
|
||||
default:
|
||||
time, err := parseutil.ParseAbsoluteTime(inp)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return time, true, nil
|
||||
}
|
||||
|
||||
case TypeCommaIntSlice:
|
||||
var result []int
|
||||
config := &mapstructure.DecoderConfig{
|
||||
|
||||
5
vendor/github.com/hashicorp/vault/sdk/framework/field_type.go
generated
vendored
5
vendor/github.com/hashicorp/vault/sdk/framework/field_type.go
generated
vendored
@@ -56,6 +56,9 @@ const (
|
||||
|
||||
// TypeFloat parses both float32 and float64 values
|
||||
TypeFloat
|
||||
|
||||
// TypeTime represents absolute time, using an RFC3999 format on the wire
|
||||
TypeTime
|
||||
)
|
||||
|
||||
func (t FieldType) String() string {
|
||||
@@ -82,6 +85,8 @@ func (t FieldType) String() string {
|
||||
return "header"
|
||||
case TypeFloat:
|
||||
return "float"
|
||||
case TypeTime:
|
||||
return "time"
|
||||
default:
|
||||
return "unknown type"
|
||||
}
|
||||
|
||||
48
vendor/github.com/hashicorp/vault/sdk/helper/parseutil/parseutil.go
generated
vendored
48
vendor/github.com/hashicorp/vault/sdk/helper/parseutil/parseutil.go
generated
vendored
@@ -67,6 +67,54 @@ func ParseDurationSecond(in interface{}) (time.Duration, error) {
|
||||
return dur, nil
|
||||
}
|
||||
|
||||
func ParseAbsoluteTime(in interface{}) (time.Time, error) {
|
||||
var t time.Time
|
||||
switch inp := in.(type) {
|
||||
case nil:
|
||||
// return default of zero
|
||||
return t, nil
|
||||
case string:
|
||||
// Allow RFC3339 with nanoseconds, or without,
|
||||
// or an epoch time as an integer.
|
||||
var err error
|
||||
t, err = time.Parse(time.RFC3339Nano, inp)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
t, err = time.Parse(time.RFC3339, inp)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
epochTime, err := strconv.ParseInt(inp, 10, 64)
|
||||
if err == nil {
|
||||
t = time.Unix(epochTime, 0)
|
||||
break
|
||||
}
|
||||
return t, errors.New("could not parse string as date and time")
|
||||
case json.Number:
|
||||
epochTime, err := inp.Int64()
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
t = time.Unix(epochTime, 0)
|
||||
case int:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
case int32:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
case int64:
|
||||
t = time.Unix(inp, 0)
|
||||
case uint:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
case uint32:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
case uint64:
|
||||
t = time.Unix(int64(inp), 0)
|
||||
default:
|
||||
return t, errors.New("could not parse time from input type")
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func ParseInt(in interface{}) (int64, error) {
|
||||
var ret int64
|
||||
jsonIn, ok := in.(json.Number)
|
||||
|
||||
Reference in New Issue
Block a user