Skip to content

Commit b6445c7

Browse files
committed
next version preparation: hero: add a Container.Inject method to inject values outside of HTTP lifecycle, e.g. a database may be used by other services outside of Iris, the hero container (and API's Builder.GetContainer()) should provide it.
Former-commit-id: 89863055a3a3ab108a3f4b753072a35321a3a193
1 parent 5ee06f9 commit b6445c7

File tree

4 files changed

+133
-14
lines changed

4 files changed

+133
-14
lines changed

hero/binding.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,15 @@ func getBindingsFor(inputs []reflect.Type, deps []*Dependency, paramsCount int)
125125
// Therefore, count the inputs that can be path parameters first.
126126
shouldBindParams := make(map[int]struct{})
127127
totalParamsExpected := 0
128-
for i, in := range inputs {
129-
if _, canBePathParameter := context.ParamResolvers[in]; !canBePathParameter {
130-
continue
131-
}
132-
shouldBindParams[i] = struct{}{}
128+
if paramsCount != -1 {
129+
for i, in := range inputs {
130+
if _, canBePathParameter := context.ParamResolvers[in]; !canBePathParameter {
131+
continue
132+
}
133+
shouldBindParams[i] = struct{}{}
133134

134-
totalParamsExpected++
135+
totalParamsExpected++
136+
}
135137
}
136138

137139
startParamIndex := paramsCount - totalParamsExpected

hero/container.go

+69
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package hero
22

33
import (
44
stdContext "context"
5+
"errors"
56
"net/http"
7+
"reflect"
68
"time"
79

810
"github.com/kataras/iris/v12/context"
@@ -177,3 +179,70 @@ func (c *Container) HandlerWithParams(fn interface{}, paramsCount int) context.H
177179
func (c *Container) Struct(ptrValue interface{}, partyParamsCount int) *Struct {
178180
return makeStruct(ptrValue, c, partyParamsCount)
179181
}
182+
183+
/*
184+
func (c *Container) Inject(ctx context.Context, toPtr ...interface{}) error {
185+
types := make([]reflect.Type, 0, len(toPtr))
186+
for _, ptr := range toPtr {
187+
types = append(types, indirectType(typeOf(ptr)))
188+
}
189+
190+
bindings := getBindingsFor(types, c.Dependencies, -1)
191+
192+
for _, b := range bindings {
193+
v, err := b.Dependency.Handle(ctx, b.Input)
194+
if err != nil {
195+
if err == ErrSeeOther {
196+
continue
197+
}
198+
199+
return err
200+
}
201+
202+
reflect.ValueOf(toPtr).Set(v)
203+
}
204+
205+
return nil
206+
}*/
207+
208+
// ErrMissingDependency may returned only from the `Container.Inject` method
209+
// when not a matching dependency found for "toPtr".
210+
var ErrMissingDependency = errors.New("missing dependency")
211+
212+
// Inject SHOULD only be used outside of HTTP handlers (performance is not priority for this method)
213+
// as it does not pre-calculate the available list of bindings for the "toPtr" and the registered dependencies.
214+
//
215+
// It sets a static-only matching dependency to the value of "toPtr".
216+
// The parameter "toPtr" SHOULD be a pointer to a value corresponding to a dependency,
217+
// like input parameter of a handler or field of a struct.
218+
//
219+
// If no matching dependency found, the `Inject` method returns an `ErrMissingDependency` and
220+
// "toPtr" keeps its original state (e.g. nil).
221+
//
222+
// Example Code:
223+
// c.Register(&LocalDatabase{...})
224+
// [...]
225+
// var db Database
226+
// err := c.Inject(&db)
227+
func (c *Container) Inject(toPtr interface{}) error {
228+
val := reflect.Indirect(valueOf(toPtr))
229+
typ := val.Type()
230+
231+
for _, d := range c.Dependencies {
232+
if d.Static && matchDependency(d, typ) {
233+
v, err := d.Handle(nil, &Input{Type: typ})
234+
if err != nil {
235+
if err == ErrSeeOther {
236+
continue
237+
}
238+
239+
return err
240+
}
241+
242+
val.Set(v)
243+
return nil
244+
}
245+
}
246+
247+
return ErrMissingDependency
248+
}

hero/container_test.go

+43-3
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,54 @@ var (
4646
}
4747
)
4848

49-
func TestHeroHandler(t *testing.T) {
49+
func TestContainerHandler(t *testing.T) {
5050
app := iris.New()
5151

52-
b := New()
53-
postHandler := b.Handler(fn)
52+
c := New()
53+
postHandler := c.Handler(fn)
5454
app.Post("/{id:int}", postHandler)
5555

5656
e := httptest.New(t, app)
5757
path := fmt.Sprintf("/%d", expectedOutput.ID)
5858
e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
5959
}
60+
61+
func TestContainerInject(t *testing.T) {
62+
c := New()
63+
64+
expected := testInput{Name: "test"}
65+
c.Register(expected)
66+
c.Register(&expected)
67+
68+
// struct value.
69+
var got1 testInput
70+
if err := c.Inject(&got1); err != nil {
71+
t.Fatal(err)
72+
}
73+
if !reflect.DeepEqual(expected, got1) {
74+
t.Fatalf("[struct value] expected: %#+v but got: %#+v", expected, got1)
75+
}
76+
77+
// ptr.
78+
var got2 *testInput
79+
if err := c.Inject(&got2); err != nil {
80+
t.Fatal(err)
81+
}
82+
83+
if !reflect.DeepEqual(&expected, got2) {
84+
t.Fatalf("[ptr] expected: %#+v but got: %#+v", &expected, got2)
85+
}
86+
87+
// register implementation, expect interface.
88+
expected3 := &testServiceImpl{prefix: "prefix: "}
89+
c.Register(expected3)
90+
91+
var got3 testService
92+
if err := c.Inject(&got3); err != nil {
93+
t.Fatal(err)
94+
}
95+
96+
if !reflect.DeepEqual(expected3, got3) {
97+
t.Fatalf("[service] expected: %#+v but got: %#+v", expected3, got3)
98+
}
99+
}

hero/reflect.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,23 @@ import (
77
)
88

99
func valueOf(v interface{}) reflect.Value {
10-
if v, ok := v.(reflect.Value); ok {
10+
if val, ok := v.(reflect.Value); ok {
1111
// check if it's already a reflect.Value.
12-
return v
12+
return val
1313
}
1414

1515
return reflect.ValueOf(v)
1616
}
1717

18+
func typeOf(typ interface{}) reflect.Type {
19+
if v, ok := typ.(reflect.Type); ok {
20+
// check if it's already a reflect.Type.
21+
return v
22+
}
23+
24+
return reflect.TypeOf(typ)
25+
}
26+
1827
// indirectType returns the value of a pointer-type "typ".
1928
// If "typ" is a pointer, array, chan, map or slice it returns its Elem,
2029
// otherwise returns the typ as it's.
@@ -82,9 +91,8 @@ func equalTypes(binding reflect.Type, input reflect.Type) bool {
8291
// if accepts an interface, check if the given "got" type does
8392
// implement this "expected" user handler's input argument.
8493
if input.Kind() == reflect.Interface {
85-
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", expected.String(), got.String())
86-
// return got.Implements(expected)
87-
// return expected.AssignableTo(got)
94+
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", binding.String(), input.String())
95+
// return input.Implements(binding)
8896
return binding.AssignableTo(input)
8997
}
9098

0 commit comments

Comments
 (0)