From 1bb5ef7484dd90b07a1326a0adec5dd0e1d9e9fc Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 16 Oct 2024 09:36:09 -0600 Subject: [PATCH 1/4] fix(docs): guard against nil --- pkg/docs/generate.go | 4 ++++ pkg/docs/generate_test.go | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/pkg/docs/generate.go b/pkg/docs/generate.go index feae9a8..7a384ae 100644 --- a/pkg/docs/generate.go +++ b/pkg/docs/generate.go @@ -9,6 +9,10 @@ import ( func GeneratePropertiesMap(data interface{}) map[string]string { properties := map[string]string{} + if data == nil { + return properties + } + v := reflect.ValueOf(data) if v.Kind() == reflect.Ptr { v = v.Elem() diff --git a/pkg/docs/generate_test.go b/pkg/docs/generate_test.go index b5c4aa9..f9c0b95 100644 --- a/pkg/docs/generate_test.go +++ b/pkg/docs/generate_test.go @@ -27,6 +27,12 @@ func TestGenerateProperties(t *testing.T) { skipped string //nolint:unused } + type TestResource4 struct { + name string + ignore string + example string + } + cases := []struct { name string in interface{} @@ -70,6 +76,11 @@ func TestGenerateProperties(t *testing.T) { "Delta": "A property rename", }, }, + { + name: "TestResource4", + in: TestResource4{}, + want: map[string]string{}, + }, } for _, c := range cases { From be95bb700a27b64a94c592a5f913aa9c27b53176 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 16 Oct 2024 09:36:31 -0600 Subject: [PATCH 2/4] feat(filter): add dateOlderThanNow --- pkg/filter/README.md | 60 +++++++++++++++++++++++++++++++++++---- pkg/filter/filter.go | 38 ++++++++++++++++++------- pkg/filter/filter_test.go | 21 ++++++++++++++ 3 files changed, 104 insertions(+), 15 deletions(-) diff --git a/pkg/filter/README.md b/pkg/filter/README.md index 9fe3bff..5d7678b 100644 --- a/pkg/filter/README.md +++ b/pkg/filter/README.md @@ -8,6 +8,15 @@ different `group` are combined with an `OR` operation. There is also the concept of a `global` filter that is applied to all resources. +## Global + +You can define a global filter that will be applied to all resources. This is useful for defining a set of filters that +should be applied to all resources. + +It has a special key called `__global__`. + +This only works when you are defining it as a resource type as part of the `Filters` `map[string][]Filter` type. + ## Types There are multiple filter types that can be used to filter the resources. These types are used to match against the @@ -19,14 +28,55 @@ property. - regex - contains - dateOlderThan +- dateOlderThanNow - suffix - prefix +- In +- NotIn -## Global +### empty / exact -You can define a global filter that will be applied to all resources. This is useful for defining a set of filters that -should be applied to all resources. +These are identical, if you leave your type empty, it will choose exact. Exact will only match if values are identical. -It has a special key called `__global__`. +### glob + +A glob allows for matching values using asterisk for a wild card, you may have more than one asterisk. + +### regex + +A regex allows for matching values with any valid regular expression. + +### contains + +A contains type allows for matching a value if it has the value contained within the property value. + +### dateOlderThan + +This allows you to filter a property's value based on whether it is older + +### dateOlderThanNow + +This allows you to filter a properties value based on whether it is older than the current time in UTC with an addition +or subtraction of a duration value. + +For example if the property is `CreatedDate` and the value is `2024-04-14T12:00:00Z` and the current time is +`2024-04-14T18:00:00Z` then you can set a negative duration like `-12h`. In this case it would not match, as the +`CreatedDate` would be **after** the adjusted time. + +If you adjusted it `-4h` then it **would** match as the `CreatedDate` would be **before** the adjusted time. + +### suffix + +This allows you to match a value if the value being filtered on is the suffix of the property value. + +### prefix + +This allows you to match a value if the value being filtered on is the prefix of the property value. + +### In + +This allows you to match a value if it is in a list of values. + +### NotIn -This only works when you are defining it as a resource type as part of the `Filters` `map[string][]Filter` type. \ No newline at end of file +This allows you to match a value if it is not in a list of values. \ No newline at end of file diff --git a/pkg/filter/filter.go b/pkg/filter/filter.go index 8a788ab..97fc973 100644 --- a/pkg/filter/filter.go +++ b/pkg/filter/filter.go @@ -15,16 +15,17 @@ import ( type Type string const ( - Empty Type = "" - Exact Type = "exact" - Glob Type = "glob" - Regex Type = "regex" - Contains Type = "contains" - DateOlderThan Type = "dateOlderThan" - Suffix Type = "suffix" - Prefix Type = "prefix" - NotIn Type = "NotIn" - In Type = "In" + Empty Type = "" + Exact Type = "exact" + Glob Type = "glob" + Regex Type = "regex" + Contains Type = "contains" + DateOlderThan Type = "dateOlderThan" + DateOlderThanNow Type = "dateOlderThanNow" + Suffix Type = "suffix" + Prefix Type = "prefix" + NotIn Type = "NotIn" + In Type = "In" Global = "__global__" ) @@ -140,6 +141,23 @@ func (f *Filter) Match(o string) (bool, error) { return fieldTimeWithOffset.After(time.Now()), nil + case DateOlderThanNow: + if o == "" { + return false, nil + } + duration, err := time.ParseDuration(f.Value) + if err != nil { + return false, err + } + fieldTime, err := parseDate(o) + if err != nil { + return false, err + } + + adjustedTime := time.Now().UTC().Add(duration) + + return adjustedTime.After(fieldTime), nil + case Prefix: return strings.HasPrefix(o, f.Value), nil diff --git a/pkg/filter/filter_test.go b/pkg/filter/filter_test.go index dd741d8..f285b9a 100644 --- a/pkg/filter/filter_test.go +++ b/pkg/filter/filter_test.go @@ -201,6 +201,27 @@ func TestFilter_UnmarshalFilter(t *testing.T) { }, error: true, }, + { + name: "dateOlderThanNow", + yaml: `{"type":"dateOlderThanNow","value":"0"}`, + match: []string{ + past.Format(time.RFC3339), + }, + mismatch: []string{ + future.Format(time.RFC3339), + }, + }, + { + name: "dateOlderThanNow2", + yaml: `{"type": "dateOlderThanNow", "value": "-36h"}`, // -36 hours + match: []string{ + past.Add(-13 * time.Hour).Format(time.RFC3339), + }, + mismatch: []string{ + past.Format(time.RFC3339), // -24 hours + future.Format(time.RFC3339), // +24 hours + }, + }, { yaml: `{"type":"prefix","value":"someprefix-"}`, match: []string{"someprefix-1234", "someprefix-someprefix", "someprefix-asdafd"}, From 9c29da040f994b76368ece04c46467d4a358ae84 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 16 Oct 2024 10:54:14 -0600 Subject: [PATCH 3/4] test(docs): add coverage for nil --- pkg/docs/generate_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/docs/generate_test.go b/pkg/docs/generate_test.go index f9c0b95..90198d1 100644 --- a/pkg/docs/generate_test.go +++ b/pkg/docs/generate_test.go @@ -81,6 +81,11 @@ func TestGenerateProperties(t *testing.T) { in: TestResource4{}, want: map[string]string{}, }, + { + name: "nil", + in: nil, + want: map[string]string{}, + }, } for _, c := range cases { From ff6e6009f4d82b711e51c3c56c523c5f73b3798a Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 16 Oct 2024 10:56:06 -0600 Subject: [PATCH 4/4] chore: fix golangci-lint violations --- pkg/docs/generate_test.go | 6 +++--- pkg/filter/filter.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/docs/generate_test.go b/pkg/docs/generate_test.go index 90198d1..69bd293 100644 --- a/pkg/docs/generate_test.go +++ b/pkg/docs/generate_test.go @@ -28,9 +28,9 @@ func TestGenerateProperties(t *testing.T) { } type TestResource4 struct { - name string - ignore string - example string + name string //nolint:unused + ignore string //nolint:unused + example string //nolint:unused } cases := []struct { diff --git a/pkg/filter/filter.go b/pkg/filter/filter.go index 97fc973..a3b72dc 100644 --- a/pkg/filter/filter.go +++ b/pkg/filter/filter.go @@ -107,7 +107,7 @@ func (f *Filter) Validate() error { } // Match checks if the filter matches the given value -func (f *Filter) Match(o string) (bool, error) { +func (f *Filter) Match(o string) (bool, error) { //nolint:gocyclo switch f.Type { case Empty, Exact: return f.Value == o, nil