mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-03 03:58:01 +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{}
|
return http.Header{}
|
||||||
case TypeFloat:
|
case TypeFloat:
|
||||||
return 0.0
|
return 0.0
|
||||||
|
case TypeTime:
|
||||||
|
return time.Time{}
|
||||||
default:
|
default:
|
||||||
panic("unknown type: " + t.String())
|
panic("unknown type: " + t.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func (d *FieldData) Validate() error {
|
|||||||
switch schema.Type {
|
switch schema.Type {
|
||||||
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
||||||
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
||||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat:
|
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat, TypeTime:
|
||||||
_, _, err := d.getPrimitive(field, schema)
|
_, _, err := d.getPrimitive(field, schema)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errwrap.Wrapf(fmt.Sprintf("error converting input %v for field %q: {{err}}", value, field), err)
|
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 {
|
switch schema.Type {
|
||||||
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
||||||
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
||||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat:
|
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat, TypeTime:
|
||||||
return d.getPrimitive(k, schema)
|
return d.getPrimitive(k, schema)
|
||||||
default:
|
default:
|
||||||
return nil, false,
|
return nil, false,
|
||||||
@@ -221,6 +221,19 @@ func (d *FieldData) getPrimitive(k string, schema *FieldSchema) (interface{}, bo
|
|||||||
}
|
}
|
||||||
return result, true, nil
|
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:
|
case TypeCommaIntSlice:
|
||||||
var result []int
|
var result []int
|
||||||
config := &mapstructure.DecoderConfig{
|
config := &mapstructure.DecoderConfig{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFieldDataGet(t *testing.T) {
|
func TestFieldDataGet(t *testing.T) {
|
||||||
@@ -931,6 +932,40 @@ func TestFieldDataGet(t *testing.T) {
|
|||||||
0.0,
|
0.0,
|
||||||
true,
|
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 {
|
for name, tc := range cases {
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ const (
|
|||||||
|
|
||||||
// TypeFloat parses both float32 and float64 values
|
// TypeFloat parses both float32 and float64 values
|
||||||
TypeFloat
|
TypeFloat
|
||||||
|
|
||||||
|
// TypeTime represents absolute time, using an RFC3999 format on the wire
|
||||||
|
TypeTime
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t FieldType) String() string {
|
func (t FieldType) String() string {
|
||||||
@@ -82,6 +85,8 @@ func (t FieldType) String() string {
|
|||||||
return "header"
|
return "header"
|
||||||
case TypeFloat:
|
case TypeFloat:
|
||||||
return "float"
|
return "float"
|
||||||
|
case TypeTime:
|
||||||
|
return "time"
|
||||||
default:
|
default:
|
||||||
return "unknown type"
|
return "unknown type"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,54 @@ func ParseDurationSecond(in interface{}) (time.Duration, error) {
|
|||||||
return dur, nil
|
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) {
|
func ParseInt(in interface{}) (int64, error) {
|
||||||
var ret int64
|
var ret int64
|
||||||
jsonIn, ok := in.(json.Number)
|
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) {
|
func Test_ParseBool(t *testing.T) {
|
||||||
outp, err := ParseBool("true")
|
outp, err := ParseBool("true")
|
||||||
if err != nil {
|
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{}
|
return http.Header{}
|
||||||
case TypeFloat:
|
case TypeFloat:
|
||||||
return 0.0
|
return 0.0
|
||||||
|
case TypeTime:
|
||||||
|
return time.Time{}
|
||||||
default:
|
default:
|
||||||
panic("unknown type: " + t.String())
|
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 {
|
switch schema.Type {
|
||||||
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
||||||
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
||||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat:
|
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat, TypeTime:
|
||||||
_, _, err := d.getPrimitive(field, schema)
|
_, _, err := d.getPrimitive(field, schema)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errwrap.Wrapf(fmt.Sprintf("error converting input %v for field %q: {{err}}", value, field), err)
|
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 {
|
switch schema.Type {
|
||||||
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString,
|
||||||
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
|
||||||
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat:
|
TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat, TypeTime:
|
||||||
return d.getPrimitive(k, schema)
|
return d.getPrimitive(k, schema)
|
||||||
default:
|
default:
|
||||||
return nil, false,
|
return nil, false,
|
||||||
@@ -221,6 +221,19 @@ func (d *FieldData) getPrimitive(k string, schema *FieldSchema) (interface{}, bo
|
|||||||
}
|
}
|
||||||
return result, true, nil
|
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:
|
case TypeCommaIntSlice:
|
||||||
var result []int
|
var result []int
|
||||||
config := &mapstructure.DecoderConfig{
|
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 parses both float32 and float64 values
|
||||||
TypeFloat
|
TypeFloat
|
||||||
|
|
||||||
|
// TypeTime represents absolute time, using an RFC3999 format on the wire
|
||||||
|
TypeTime
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t FieldType) String() string {
|
func (t FieldType) String() string {
|
||||||
@@ -82,6 +85,8 @@ func (t FieldType) String() string {
|
|||||||
return "header"
|
return "header"
|
||||||
case TypeFloat:
|
case TypeFloat:
|
||||||
return "float"
|
return "float"
|
||||||
|
case TypeTime:
|
||||||
|
return "time"
|
||||||
default:
|
default:
|
||||||
return "unknown type"
|
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
|
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) {
|
func ParseInt(in interface{}) (int64, error) {
|
||||||
var ret int64
|
var ret int64
|
||||||
jsonIn, ok := in.(json.Number)
|
jsonIn, ok := in.(json.Number)
|
||||||
|
|||||||
Reference in New Issue
Block a user