Enable trigger filter to be optional#603
Conversation
rhuss
left a comment
There was a problem hiding this comment.
Looks good, with some minor cosmetic suggestions.
Thanks !
|
|
||
| trigger := constructTrigger(name, namespace, triggerUpdateFlags.Broker, filters) | ||
| trigger.Spec.Subscriber = &duckv1.Destination{ | ||
| trigger := client_v1alpha1.NewTriggerBuilder(name) |
There was a problem hiding this comment.
better call it triggerBuilder
| trigger := constructTrigger(name, namespace, triggerUpdateFlags.Broker, filters) | ||
| trigger.Spec.Subscriber = &duckv1.Destination{ | ||
| trigger := client_v1alpha1.NewTriggerBuilder(name) | ||
| trigger.Namespace(namespace) |
There was a problem hiding this comment.
you can chain the calls directly as in
triggerBuilder := NewTriggerBuilder(name).Namespace(...).Broker(....).Subscriber(....)| trigger := client_v1alpha1.NewTriggerBuilder(name) | ||
| trigger.Namespace(namespace) | ||
| trigger.Broker(triggerUpdateFlags.Broker) | ||
| for k, v := range filters { |
There was a problem hiding this comment.
We could conside to add a Filters(map[string]string) builder method so you could easily do the full chaining (and providers a nicer API for the builder)
rhuss
left a comment
There was a problem hiding this comment.
Thanks, but I have still some nit-picks around the builder API (see below)
| } | ||
|
|
||
| func (b *TriggerBuilder) AddFilter(key, value string) *TriggerBuilder { | ||
| func (b *TriggerBuilder) AddSingleFilter(key, value string) *TriggerBuilder { |
There was a problem hiding this comment.
I'd suggest to keep AddFilter and jut add a Filters method for setting the full field (like for the other methods)
There was a problem hiding this comment.
In the latest version, I keep only Filters to add multiple filters.
| return b | ||
| } | ||
|
|
||
| func (b *TriggerBuilder) RemoveMultipleFilters(keys []string) *TriggerBuilder { |
There was a problem hiding this comment.
Do wee need this ? (and also RemoveFilter). At the end its a builder with a very short lifetime. If you don't want to have something added, just don't add it. IMO, we should only have methods which 'build up' something.
There was a problem hiding this comment.
The remove filter will remove existing filters setting by --filter name-.
If I need to remove some existing filters, what should I do ? Get the existing map of filters, then remove some keys outside builder, and then set back to builder ?
There was a problem hiding this comment.
I keep only RemoveFilters in the latest version to remove those filters from existing attributes setting by --filter name-
| for _, k := range removed { | ||
| b.RemoveFilter(k) | ||
| } | ||
| b.Filters(updated).RemoveFilters(removed) |
There was a problem hiding this comment.
Sorry, my last nit-pick (promised :). What do you think about:
b.Filters(maputil.RemoveKeysFromMap(updated, removed))and a maputil.RemoveFrom() (but maybe in an already existing util package):
func RemoveKeysFromMap(original map[string]string, toRemove []string) map[string]string {
....
}That way this method could be re-used for other use cases and we don't have to add a Remove method to a builder.
wdyt ?
There was a problem hiding this comment.
never mind. Thank you for the suggestion.
The problem of your suggestion is that I need to get the original value of filters map to remove. I think we want to hide the detail of trigger Spec by Builder. The value of filters map is Spec.Filter.Attributes, and Spec.Filter may be nil at the beginning. If I don't add a method GetFilters() to Builder, I have to manage the trigger Spec structure to get the filter. So my code may like below:
var attributes *map[string]string
attributes = nil
if b.Build().Spec.Filter != nil {
attributes = b.Build().Spec.Filter.Attributes
}
b.Filters(maputil.AddAndRemoveFromMap(attributes, updated,removed))
and a method to the util:
func AddAndRemoveFromMap(original *map[string]string, toAdd map[string]string, toRemove []string) map[string]string {
......
}
If I add a GetFilters() to Builder, it may be much simpler:
b.Filters(maputil.AddAndRemoveFromMap(b.GetFilters(), updated,removed))
but it violates the rule that builder is to build something.
Do I make sense ? What's your option?
There was a problem hiding this comment.
My suggestion:
// Extract filters directly from original trigger (not builder). Return nil if none.
existing := extractFilters(trigger)
// Use chained util method, so that they can bey reused in different contexts:
// * type StringMap map[string]string
// * func StringMap(map [string]string) StringMap : Just cast or copy to the new type (then call it "NewStringMap")
// * func (m SringMap) Merge(toMerge map[string]string) StringMap
// Keys from the toMerge map override those from the wrapped map
// * func (m map[string]string) RemovKeys(toRemove string[]) for removing the keys of a map. If this no
b.Filters(maputil.StringMap(existing).Merge(updated).Remove(removed))This would have the benefit:
- Builder is still immutable and write-only
- Reusable string map manipulation functions which can be easily used in different contexts
- Easy to use API (maybe a bit harder to implement but obey one of the principles of good API design: "It should be as easy to use as possible, even when its might harder to implement that parts that are hidden from the user".
Instead of introducing a "StringMap" you could equally cast to the type "StringMap" via existing.(StringMap).Merge(updated).Remove(toRemove)
Does this make sense ?
There was a problem hiding this comment.
Thanks. I made a small update to your suggestion.
I used builder as parameter in extractFilters, not trigger. I don't like to introduce a dependency to knative.dev/eventing/pkg/apis/eventing/v1alpha1 in the update command. If I use trigger as parameter, I have to add this dependency. Here is the definition of extractFilters
func extractFilters(builder *client_v1alpha1.TriggerBuilder) util.StringMap
Is that OK with you?
There was a problem hiding this comment.
I used builder as parameter in
extractFilters, not trigger. I don't like to introduce a dependency toknative.dev/eventing/pkg/apis/eventing/v1alpha1in the update command. If I use trigger as parameter, I have to add this dependency. Here is the definition ofextractFilters
Don't we already have the trigger available in this method ? with extractFilters() I mean just a function within update.go itself (just holding the loop that you've mentioned). I don't think we should put that into the builder.
I refer to this line:
client/pkg/kn/commands/trigger/update.go
Line 69 in 5c2ac96
There was a problem hiding this comment.
Ah, you mean a goimport. Good point, but we have the implicit dependency anyway (e.g. via the constructor arg of the builder or the return value). Alternatively you could add the function to client_v1alpha1 package, like client_v1alpha1.ExtractFilters(trigger)
It's better than diving into a builder. E.g. in generally when you call Build() on a Builder this is considered often to be a final action, so you can't call Build() a second time on the same builder (because it performs some more persisten action). So I would prefer to avoid that in favor of the utility in the client package.
There was a problem hiding this comment.
Yes, we already have the implicit dependency. I added a line to goimport in update.go, and used trigger as input parameter in the latest version. Please review. Thank you.
5c2ac96 to
e2f8966
Compare
|
|
||
| func extractFilters(trigger *v1alpha1.Trigger) util.StringMap { | ||
| if trigger.Spec.Filter == nil { | ||
| return nil |
There was a problem hiding this comment.
You should return an empty map here, otherwise you can a panic when you call existing.Merge and existing is nil. Its also inconsistent to return different things when Filter == nil or Filter.Attributes == nil
There was a problem hiding this comment.
Changed it in the latest version. Thank you.
| }) | ||
|
|
||
| t.Run("update filter with only deletions", func(t *testing.T) { | ||
| t.Run("update filter with add and update", func(t *testing.T) { |
There was a problem hiding this comment.
this description reads odd... any way to make this clearer?
|
The following is the coverage report on the affected files.
|
rhuss
left a comment
There was a problem hiding this comment.
Thanks a lot ! Looks good now, and I think we have now also a nice API for builder with allowing to remove parts.
Good work!
/lgtm
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: daisy-ycguo, rhuss The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Proposed Changes