Skip to content

Commit f6b3757

Browse files
committed
feat: allow overriding namespace when calling Scan and Resolve
also... 1. removed the ReplaceDirectiveExecutor API 2. removed some error constants
1 parent ff1212b commit f6b3757

File tree

6 files changed

+49
-45
lines changed

6 files changed

+49
-45
lines changed

errors.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@ import (
88

99
var (
1010
ErrUnsupportedType = errors.New("unsupported type")
11-
ErrNilNamespace = errors.New("nil namespace")
12-
ErrInvalidDirectiveName = errors.New("invalid directive/executor name")
13-
ErrDuplicateExecutor = errors.New("duplicate executor")
11+
ErrInvalidDirectiveName = errors.New("invalid directive name")
1412
ErrDuplicateDirective = errors.New("duplicate directive")
15-
ErrNilExecutor = errors.New("nil executor")
1613
ErrMissingExecutor = errors.New("missing executor")
1714
ErrTypeMismatch = errors.New("type mismatch")
1815
ErrScanNilField = errors.New("scan nil field")
@@ -22,16 +19,16 @@ func invalidDirectiveName(name string) error {
2219
return fmt.Errorf("%w: %q (should comply with %s)", ErrInvalidDirectiveName, name, reDirectiveName.String())
2320
}
2421

25-
func duplicateExecutor(name string) error {
26-
return fmt.Errorf("%w: %q (registered to the same namespace)", ErrDuplicateExecutor, name)
27-
}
28-
2922
func duplicateDirective(name string) error {
3023
return fmt.Errorf("%w: %q (defined in the same struct tag)", ErrDuplicateDirective, name)
3124
}
3225

26+
func duplicateExecutor(name string) error {
27+
return fmt.Errorf("duplicate executor: %q (registered to the same namespace)", name)
28+
}
29+
3330
func nilExecutor(name string) error {
34-
return fmt.Errorf("%w: %q", ErrNilExecutor, name)
31+
return fmt.Errorf("nil executor: %q", name)
3532
}
3633

3734
type ResolveError struct {

helper_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,6 @@ func createNsForTrackingCtor(throwError error, verifier *ContextVerifier) (*owl.
145145
ns := owl.NewNamespace()
146146
ns.RegisterDirectiveExecutor("form", NewEchoExecutor(tracker, "form", throwError, verifier))
147147
ns.RegisterDirectiveExecutor("default", NewEchoExecutor(tracker, "default", throwError, verifier))
148-
ns.ReplaceDirectiveExecutor("env", NewEchoExecutor(tracker, "env", throwError, verifier))
148+
ns.RegisterDirectiveExecutor("env", NewEchoExecutor(tracker, "env", throwError, verifier))
149149
return ns, tracker
150150
}

namespace.go

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
package owl
22

3+
import "fmt"
4+
35
var (
46
defaultNS = NewNamespace()
57
)
68

7-
// RegisterDirectiveExecutor registers a named executor globally (in the default
8-
// namespace).
9-
func RegisterDirectiveExecutor(name string, exe DirectiveExecutor) {
10-
defaultNS.RegisterDirectiveExecutor(name, exe)
11-
}
12-
13-
// ReplaceDirectiveExecutor replaces a named executor globally (in the default
14-
// namespace). It works like RegisterDirectiveExecutor without panic on
15-
// duplicate names.
16-
func ReplaceDirectiveExecutor(name string, exe DirectiveExecutor) {
17-
defaultNS.ReplaceDirectiveExecutor(name, exe)
9+
// RegisterDirectiveExecutor registers a named executor globally, i.e. to the default
10+
// namespace.
11+
func RegisterDirectiveExecutor(name string, exe DirectiveExecutor, replace ...bool) {
12+
defaultNS.RegisterDirectiveExecutor(name, exe, replace...)
1813
}
1914

2015
// LookupExecutor returns the executor by name globally (from the default
@@ -35,24 +30,19 @@ func NewNamespace() *Namespace {
3530
}
3631
}
3732

38-
// RegisterDirectiveExecutor registers a named executor in the namespace.
39-
// The executor should implement the DirectiveExecutor interface.
40-
// Will panic if the name were taken or the executor is nil.
41-
func (ns *Namespace) RegisterDirectiveExecutor(name string, exe DirectiveExecutor) {
42-
if _, ok := ns.executors[name]; ok {
43-
panic(duplicateExecutor(name))
33+
// RegisterDirectiveExecutor registers a named executor to the namespace. The executor
34+
// should implement the DirectiveExecutor interface. Will panic if the name were taken
35+
// or the executor is nil. Pass replace (true) to ignore the name conflict.
36+
func (ns *Namespace) RegisterDirectiveExecutor(name string, exe DirectiveExecutor, replace ...bool) {
37+
force := len(replace) > 0 && replace[0]
38+
if _, ok := ns.executors[name]; ok && !force {
39+
panic(fmt.Errorf("owl: %s", duplicateExecutor(name)))
4440
}
45-
ns.ReplaceDirectiveExecutor(name, exe)
46-
}
47-
48-
// ReplaceDirectiveExecutor works like RegisterDirectiveExecutor without panicing
49-
// on duplicate names. But it will panic if the executor is nil or the name is invalid.
50-
func (ns *Namespace) ReplaceDirectiveExecutor(name string, exe DirectiveExecutor) {
5141
if exe == nil {
52-
panic(nilExecutor(name))
42+
panic(fmt.Errorf("owl: %s", nilExecutor(name)))
5343
}
5444
if !isValidDirectiveName(name) {
55-
panic(invalidDirectiveName(name))
45+
panic(fmt.Errorf("owl: %s", invalidDirectiveName(name)))
5646
}
5747
ns.executors[name] = exe
5848
}

namespace_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,41 +25,41 @@ func TestNamespace(t *testing.T) {
2525

2626
ns.RegisterDirectiveExecutor("foo", DirectiveExecutorFunc(exeFoo))
2727
assert.Equal(ns.LookupExecutor("foo").Execute(nil), errFoo)
28-
assert.PanicsWithError(duplicateExecutor("foo").Error(), func() {
28+
assert.PanicsWithError("owl: "+duplicateExecutor("foo").Error(), func() {
2929
ns.RegisterDirectiveExecutor("foo", DirectiveExecutorFunc(exeFoo))
3030
})
3131

3232
ns.RegisterDirectiveExecutor("bar", DirectiveExecutorFunc(exeBar))
3333
assert.Equal(ns.LookupExecutor("bar").Execute(nil), errBar)
34-
assert.PanicsWithError(duplicateExecutor("bar").Error(), func() {
34+
assert.PanicsWithError("owl: "+duplicateExecutor("bar").Error(), func() {
3535
ns.RegisterDirectiveExecutor("bar", DirectiveExecutorFunc(exeBar))
3636
})
3737

38-
ns.ReplaceDirectiveExecutor("foo", DirectiveExecutorFunc(exeFoo))
38+
ns.RegisterDirectiveExecutor("foo", DirectiveExecutorFunc(exeFoo), true)
3939
assert.Equal(ns.LookupExecutor("foo").Execute(nil), errFoo)
40-
ns.ReplaceDirectiveExecutor("foo", DirectiveExecutorFunc(exeBar))
40+
ns.RegisterDirectiveExecutor("foo", DirectiveExecutorFunc(exeBar), true)
4141
assert.Equal(ns.LookupExecutor("foo").Execute(nil), errBar)
4242
}
4343

4444
func TestNamespace_RegisterNilExecutor(t *testing.T) {
45-
assert.PanicsWithError(t, nilExecutor("foo").Error(), func() {
45+
assert.PanicsWithError(t, "owl: "+nilExecutor("foo").Error(), func() {
4646
NewNamespace().RegisterDirectiveExecutor("foo", nil)
4747
})
4848
}
4949

5050
func TestNamespace_RegisterInvalidName(t *testing.T) {
51-
assert.PanicsWithError(t, invalidDirectiveName(".foo").Error(), func() {
51+
assert.PanicsWithError(t, "owl: "+invalidDirectiveName(".foo").Error(), func() {
5252
NewNamespace().RegisterDirectiveExecutor(".foo", DirectiveExecutorFunc(exeFoo))
5353
})
5454
}
5555

5656
func TestDefaultNamespace(t *testing.T) {
5757
RegisterDirectiveExecutor("foo", DirectiveExecutorFunc(exeFoo))
5858
assert.Equal(t, LookupExecutor("foo").Execute(nil), errFoo)
59-
assert.PanicsWithError(t, duplicateExecutor("foo").Error(), func() {
59+
assert.PanicsWithError(t, "owl: "+duplicateExecutor("foo").Error(), func() {
6060
RegisterDirectiveExecutor("foo", DirectiveExecutorFunc(exeFoo))
6161
})
6262

63-
ReplaceDirectiveExecutor("foo", DirectiveExecutorFunc(exeBar))
63+
RegisterDirectiveExecutor("foo", DirectiveExecutorFunc(exeBar), true)
6464
assert.Equal(t, LookupExecutor("foo").Execute(nil), errBar)
6565
}

resolver.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package owl
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"reflect"
78
"strconv"
@@ -81,7 +82,7 @@ func (r *Resolver) copy() *Resolver {
8182

8283
func (r *Resolver) validate() error {
8384
if r.Namespace() == nil {
84-
return fmt.Errorf("%w: the namespace you passed through WithNamespace to owl.New is nil", ErrNilNamespace)
85+
return errors.New("nil namespace")
8586
}
8687

8788
return nil
@@ -289,6 +290,11 @@ func (root *Resolver) resolve(ctx context.Context, rootValue reflect.Value) erro
289290
func (r *Resolver) runDirectives(ctx context.Context, rv reflect.Value) error {
290291
ns := r.Namespace()
291292

293+
// The namespace can be overriden by calling Scan/Resolve with WithNamespace.
294+
if nsOverriden := ctx.Value(ckNamespace); nsOverriden != nil {
295+
ns = nsOverriden.(*Namespace)
296+
}
297+
292298
for _, directive := range r.Directives {
293299
dirRuntime := &DirectiveRuntime{
294300
Directive: directive,

resolver_test.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ func TestNew_ParsingDirectives_DuplicateDirectives(t *testing.T) {
265265
func TestNew_OptionNilNamespace(t *testing.T) {
266266
resolver, err := owl.New(struct{}{}, owl.WithNamespace(nil))
267267
assert.Nil(t, resolver)
268-
assert.ErrorIs(t, err, owl.ErrNilNamespace)
268+
assert.ErrorContains(t, err, "nil namespace")
269269
}
270270

271271
func TestNew_OptionCustomValue(t *testing.T) {
@@ -597,6 +597,17 @@ func TestScan_withOpts(t *testing.T) {
597597
assert.ErrorContains(t, err, "unexpected context value")
598598
}
599599

600+
func TestScan_overrideNamespace(t *testing.T) {
601+
resolver, err := owl.New(User{}) // using default namespace
602+
assert.NoError(t, err)
603+
err = resolver.Scan(User{})
604+
assert.Error(t, err) // should fail because default namespace has no directive executors
605+
606+
ns, _ := createNsForTracking()
607+
err = resolver.Scan(User{}, owl.WithNamespace(ns))
608+
assert.NoError(t, err) // should success because namespace is overrided
609+
}
610+
600611
func TestScan_onNilValue(t *testing.T) {
601612
resolver, err := owl.New(User{})
602613
assert.NoError(t, err)

0 commit comments

Comments
 (0)