mirror of
https://github.com/kataras/iris.git
synced 2025-03-13 21:36:28 +01:00
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
This commit is contained in:
parent
5ee06f9a92
commit
b6445c7238
|
@ -125,13 +125,15 @@ func getBindingsFor(inputs []reflect.Type, deps []*Dependency, paramsCount int)
|
|||
// Therefore, count the inputs that can be path parameters first.
|
||||
shouldBindParams := make(map[int]struct{})
|
||||
totalParamsExpected := 0
|
||||
for i, in := range inputs {
|
||||
if _, canBePathParameter := context.ParamResolvers[in]; !canBePathParameter {
|
||||
continue
|
||||
}
|
||||
shouldBindParams[i] = struct{}{}
|
||||
if paramsCount != -1 {
|
||||
for i, in := range inputs {
|
||||
if _, canBePathParameter := context.ParamResolvers[in]; !canBePathParameter {
|
||||
continue
|
||||
}
|
||||
shouldBindParams[i] = struct{}{}
|
||||
|
||||
totalParamsExpected++
|
||||
totalParamsExpected++
|
||||
}
|
||||
}
|
||||
|
||||
startParamIndex := paramsCount - totalParamsExpected
|
||||
|
|
|
@ -2,7 +2,9 @@ package hero
|
|||
|
||||
import (
|
||||
stdContext "context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
|
@ -177,3 +179,70 @@ func (c *Container) HandlerWithParams(fn interface{}, paramsCount int) context.H
|
|||
func (c *Container) Struct(ptrValue interface{}, partyParamsCount int) *Struct {
|
||||
return makeStruct(ptrValue, c, partyParamsCount)
|
||||
}
|
||||
|
||||
/*
|
||||
func (c *Container) Inject(ctx context.Context, toPtr ...interface{}) error {
|
||||
types := make([]reflect.Type, 0, len(toPtr))
|
||||
for _, ptr := range toPtr {
|
||||
types = append(types, indirectType(typeOf(ptr)))
|
||||
}
|
||||
|
||||
bindings := getBindingsFor(types, c.Dependencies, -1)
|
||||
|
||||
for _, b := range bindings {
|
||||
v, err := b.Dependency.Handle(ctx, b.Input)
|
||||
if err != nil {
|
||||
if err == ErrSeeOther {
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
reflect.ValueOf(toPtr).Set(v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}*/
|
||||
|
||||
// ErrMissingDependency may returned only from the `Container.Inject` method
|
||||
// when not a matching dependency found for "toPtr".
|
||||
var ErrMissingDependency = errors.New("missing dependency")
|
||||
|
||||
// Inject SHOULD only be used outside of HTTP handlers (performance is not priority for this method)
|
||||
// as it does not pre-calculate the available list of bindings for the "toPtr" and the registered dependencies.
|
||||
//
|
||||
// It sets a static-only matching dependency to the value of "toPtr".
|
||||
// The parameter "toPtr" SHOULD be a pointer to a value corresponding to a dependency,
|
||||
// like input parameter of a handler or field of a struct.
|
||||
//
|
||||
// If no matching dependency found, the `Inject` method returns an `ErrMissingDependency` and
|
||||
// "toPtr" keeps its original state (e.g. nil).
|
||||
//
|
||||
// Example Code:
|
||||
// c.Register(&LocalDatabase{...})
|
||||
// [...]
|
||||
// var db Database
|
||||
// err := c.Inject(&db)
|
||||
func (c *Container) Inject(toPtr interface{}) error {
|
||||
val := reflect.Indirect(valueOf(toPtr))
|
||||
typ := val.Type()
|
||||
|
||||
for _, d := range c.Dependencies {
|
||||
if d.Static && matchDependency(d, typ) {
|
||||
v, err := d.Handle(nil, &Input{Type: typ})
|
||||
if err != nil {
|
||||
if err == ErrSeeOther {
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
val.Set(v)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return ErrMissingDependency
|
||||
}
|
||||
|
|
|
@ -46,14 +46,54 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func TestHeroHandler(t *testing.T) {
|
||||
func TestContainerHandler(t *testing.T) {
|
||||
app := iris.New()
|
||||
|
||||
b := New()
|
||||
postHandler := b.Handler(fn)
|
||||
c := New()
|
||||
postHandler := c.Handler(fn)
|
||||
app.Post("/{id:int}", postHandler)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
path := fmt.Sprintf("/%d", expectedOutput.ID)
|
||||
e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
|
||||
}
|
||||
|
||||
func TestContainerInject(t *testing.T) {
|
||||
c := New()
|
||||
|
||||
expected := testInput{Name: "test"}
|
||||
c.Register(expected)
|
||||
c.Register(&expected)
|
||||
|
||||
// struct value.
|
||||
var got1 testInput
|
||||
if err := c.Inject(&got1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(expected, got1) {
|
||||
t.Fatalf("[struct value] expected: %#+v but got: %#+v", expected, got1)
|
||||
}
|
||||
|
||||
// ptr.
|
||||
var got2 *testInput
|
||||
if err := c.Inject(&got2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(&expected, got2) {
|
||||
t.Fatalf("[ptr] expected: %#+v but got: %#+v", &expected, got2)
|
||||
}
|
||||
|
||||
// register implementation, expect interface.
|
||||
expected3 := &testServiceImpl{prefix: "prefix: "}
|
||||
c.Register(expected3)
|
||||
|
||||
var got3 testService
|
||||
if err := c.Inject(&got3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected3, got3) {
|
||||
t.Fatalf("[service] expected: %#+v but got: %#+v", expected3, got3)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,23 @@ import (
|
|||
)
|
||||
|
||||
func valueOf(v interface{}) reflect.Value {
|
||||
if v, ok := v.(reflect.Value); ok {
|
||||
if val, ok := v.(reflect.Value); ok {
|
||||
// check if it's already a reflect.Value.
|
||||
return v
|
||||
return val
|
||||
}
|
||||
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
|
||||
func typeOf(typ interface{}) reflect.Type {
|
||||
if v, ok := typ.(reflect.Type); ok {
|
||||
// check if it's already a reflect.Type.
|
||||
return v
|
||||
}
|
||||
|
||||
return reflect.TypeOf(typ)
|
||||
}
|
||||
|
||||
// indirectType returns the value of a pointer-type "typ".
|
||||
// If "typ" is a pointer, array, chan, map or slice it returns its Elem,
|
||||
// otherwise returns the typ as it's.
|
||||
|
@ -82,9 +91,8 @@ func equalTypes(binding reflect.Type, input reflect.Type) bool {
|
|||
// if accepts an interface, check if the given "got" type does
|
||||
// implement this "expected" user handler's input argument.
|
||||
if input.Kind() == reflect.Interface {
|
||||
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", expected.String(), got.String())
|
||||
// return got.Implements(expected)
|
||||
// return expected.AssignableTo(got)
|
||||
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", binding.String(), input.String())
|
||||
// return input.Implements(binding)
|
||||
return binding.AssignableTo(input)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user