added support for optional Model and Record event hook tags
This commit is contained in:
@@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddAndPreAdd(t *testing.T) {
|
||||
func TestHookAddAndPreAdd(t *testing.T) {
|
||||
h := Hook[int]{}
|
||||
|
||||
if total := len(h.handlers); total != 0 {
|
||||
@@ -36,7 +36,7 @@ func TestAddAndPreAdd(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
func TestHookReset(t *testing.T) {
|
||||
h := Hook[int]{}
|
||||
|
||||
h.Reset() // should do nothing and not panic
|
||||
@@ -55,7 +55,7 @@ func TestReset(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrigger(t *testing.T) {
|
||||
func TestHookTrigger(t *testing.T) {
|
||||
err1 := errors.New("demo")
|
||||
err2 := errors.New("demo")
|
||||
|
||||
@@ -92,7 +92,7 @@ func TestTrigger(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTriggerStopPropagation(t *testing.T) {
|
||||
func TestHookTriggerStopPropagation(t *testing.T) {
|
||||
called1 := false
|
||||
f1 := func(data int) error { called1 = true; return nil }
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
)
|
||||
|
||||
// Tagger defines an interface for event data structs that support tags/groups/categories/etc.
|
||||
// Usually used together with TaggedHook.
|
||||
type Tagger interface {
|
||||
Tags() []string
|
||||
}
|
||||
|
||||
// wrapped local Hook embedded struct to limit the public API surface.
|
||||
type mainHook[T Tagger] struct {
|
||||
*Hook[T]
|
||||
}
|
||||
|
||||
// NewTaggedHook creates a new TaggedHook with the provided main hook and optional tags.
|
||||
func NewTaggedHook[T Tagger](hook *Hook[T], tags ...string) *TaggedHook[T] {
|
||||
return &TaggedHook[T]{
|
||||
mainHook[T]{hook},
|
||||
tags,
|
||||
}
|
||||
}
|
||||
|
||||
// TaggedHook defines a proxy hook which register handlers that are triggered only
|
||||
// if the TaggedHook.tags are empty or includes at least one of the event data tag(s).
|
||||
type TaggedHook[T Tagger] struct {
|
||||
mainHook[T]
|
||||
|
||||
tags []string
|
||||
}
|
||||
|
||||
// CanTriggerOn checks if the current TaggedHook can be triggered with
|
||||
// the provided event data tags.
|
||||
func (p *TaggedHook[T]) CanTriggerOn(tags []string) bool {
|
||||
if len(p.tags) == 0 {
|
||||
return true // match all
|
||||
}
|
||||
|
||||
for _, t := range tags {
|
||||
if list.ExistInSlice(t, p.tags) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// PreAdd registers a new handler to the hook by prepending it to the existing queue.
|
||||
//
|
||||
// The fn handler will be called only if the event data tags satisfy p.CanTriggerOn.
|
||||
func (p *TaggedHook[T]) PreAdd(fn Handler[T]) {
|
||||
p.mainHook.PreAdd(func(e T) error {
|
||||
if p.CanTriggerOn(e.Tags()) {
|
||||
return fn(e)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Add registers a new handler to the hook by appending it to the existing queue.
|
||||
//
|
||||
// The fn handler will be called only if the event data tags satisfy p.CanTriggerOn.
|
||||
func (p *TaggedHook[T]) Add(fn Handler[T]) {
|
||||
p.mainHook.Add(func(e T) error {
|
||||
if p.CanTriggerOn(e.Tags()) {
|
||||
return fn(e)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package hook
|
||||
|
||||
import "testing"
|
||||
|
||||
type mockTagsData struct {
|
||||
tags []string
|
||||
}
|
||||
|
||||
func (m mockTagsData) Tags() []string {
|
||||
return m.tags
|
||||
}
|
||||
|
||||
func TestTaggedHook(t *testing.T) {
|
||||
triggerSequence := ""
|
||||
|
||||
base := &Hook[mockTagsData]{}
|
||||
base.Add(func(data mockTagsData) error { triggerSequence += "f0"; return nil })
|
||||
|
||||
hA := NewTaggedHook(base)
|
||||
hA.Add(func(data mockTagsData) error { triggerSequence += "a1"; return nil })
|
||||
hA.PreAdd(func(data mockTagsData) error { triggerSequence += "a2"; return nil })
|
||||
|
||||
hB := NewTaggedHook(base, "b1", "b2")
|
||||
hB.Add(func(data mockTagsData) error { triggerSequence += "b1"; return nil })
|
||||
hB.PreAdd(func(data mockTagsData) error { triggerSequence += "b2"; return nil })
|
||||
|
||||
hC := NewTaggedHook(base, "c1", "c2")
|
||||
hC.Add(func(data mockTagsData) error { triggerSequence += "c1"; return nil })
|
||||
hC.PreAdd(func(data mockTagsData) error { triggerSequence += "c2"; return nil })
|
||||
|
||||
scenarios := []struct {
|
||||
data mockTagsData
|
||||
expectedSequence string
|
||||
}{
|
||||
{
|
||||
mockTagsData{},
|
||||
"a2f0a1",
|
||||
},
|
||||
{
|
||||
mockTagsData{[]string{"missing"}},
|
||||
"a2f0a1",
|
||||
},
|
||||
{
|
||||
mockTagsData{[]string{"b2"}},
|
||||
"b2a2f0a1b1",
|
||||
},
|
||||
{
|
||||
mockTagsData{[]string{"c1"}},
|
||||
"c2a2f0a1c1",
|
||||
},
|
||||
{
|
||||
mockTagsData{[]string{"b1", "c2"}},
|
||||
"c2b2a2f0a1b1c1",
|
||||
},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
triggerSequence = "" // reset
|
||||
|
||||
err := hA.Trigger(s.data)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] Unexpected trigger error: %v", i, err)
|
||||
}
|
||||
|
||||
if triggerSequence != s.expectedSequence {
|
||||
t.Fatalf("[%d] Expected trigger sequence %s, got %s", i, s.expectedSequence, triggerSequence)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user