Add dynamic handler management #13

Merged
xdrm-brackets merged 12 commits from feature/dynamic-handler-signature into 0.3.0 2020-03-29 15:10:06 +00:00
3 changed files with 31 additions and 36 deletions
Showing only changes of commit a3daab7de4 - Show all commits

View File

@ -14,11 +14,8 @@ const ErrHandlerNotFunc = cerr("handler must be a func")
// ErrNoServiceForHandler - no service matching this handler // ErrNoServiceForHandler - no service matching this handler
const ErrNoServiceForHandler = cerr("no service found for this handler") const ErrNoServiceForHandler = cerr("no service found for this handler")
// ErrMissingHandlerArgument - missing arguments for handler
const ErrMissingHandlerArgument = cerr("handler must have at least 1 arguments")
// ErrMissingHandlerArgumentParam - missing params arguments for handler // ErrMissingHandlerArgumentParam - missing params arguments for handler
const ErrMissingHandlerArgumentParam = cerr("missing handler argument 2 : parameter struct") const ErrMissingHandlerArgumentParam = cerr("missing handler argument : parameter struct")
// ErrMissingHandlerOutput - missing output for handler // ErrMissingHandlerOutput - missing output for handler
const ErrMissingHandlerOutput = cerr("handler must have at least 1 output") const ErrMissingHandlerOutput = cerr("handler must have at least 1 output")
@ -33,7 +30,7 @@ const ErrMissingRequestArgument = cerr("handler first argument must be of type a
const ErrMissingParamArgument = cerr("handler second argument must be a struct") const ErrMissingParamArgument = cerr("handler second argument must be a struct")
// ErrMissingParamOutput - missing output argument for handler // ErrMissingParamOutput - missing output argument for handler
const ErrMissingParamOutput = cerr("handler first output must be a struct") const ErrMissingParamOutput = cerr("handler first output must be a *struct")
// ErrMissingParamFromConfig - missing a parameter in handler struct // ErrMissingParamFromConfig - missing a parameter in handler struct
const ErrMissingParamFromConfig = cerr("missing a parameter from configuration") const ErrMissingParamFromConfig = cerr("missing a parameter from configuration")
@ -42,10 +39,10 @@ const ErrMissingParamFromConfig = cerr("missing a parameter from configuration")
const ErrMissingOutputFromConfig = cerr("missing a parameter from configuration") const ErrMissingOutputFromConfig = cerr("missing a parameter from configuration")
// ErrWrongParamTypeFromConfig - a configuration parameter type is invalid in the handler param struct // ErrWrongParamTypeFromConfig - a configuration parameter type is invalid in the handler param struct
const ErrWrongParamTypeFromConfig = cerr("invalid parameter struct type from configuration") const ErrWrongParamTypeFromConfig = cerr("invalid struct field type")
// ErrWrongOutputTypeFromConfig - a configuration output type is invalid in the handler output struct // ErrWrongOutputTypeFromConfig - a configuration output type is invalid in the handler output struct
const ErrWrongOutputTypeFromConfig = cerr("invalid output struct type from configuration") const ErrWrongOutputTypeFromConfig = cerr("invalid struct field type")
// ErrMissingHandlerErrorOutput - missing handler output error // ErrMissingHandlerErrorOutput - missing handler output error
const ErrMissingHandlerErrorOutput = cerr("last output must be of type api.Error") const ErrMissingHandlerErrorOutput = cerr("last output must be of type api.Error")

View File

@ -22,16 +22,16 @@ func Build(fn HandlerFn, service config.Service) (*Handler, error) {
fn: fn, fn: fn,
} }
fnt := reflect.TypeOf(fn) fnv := reflect.ValueOf(fn)
if fnt.Kind() != reflect.Func { if fnv.Type().Kind() != reflect.Func {
return nil, ErrHandlerNotFunc return nil, ErrHandlerNotFunc
} }
if err := h.spec.checkInput(fnt); err != nil { if err := h.spec.checkInput(fnv); err != nil {
return nil, fmt.Errorf("input: %w", err) return nil, fmt.Errorf("input: %w", err)
} }
if err := h.spec.checkOutput(fnt); err != nil { if err := h.spec.checkOutput(fnv); err != nil {
return nil, fmt.Errorf("output: %w", err) return nil, fmt.Errorf("output: %w", err)
} }

View File

@ -25,11 +25,6 @@ func makeSpec(service config.Service) spec {
} }
for _, param := range service.Output { for _, param := range service.Output {
// make a pointer if optional
if param.Optional {
spec.Output[param.Rename] = reflect.PtrTo(param.ExtractType)
continue
}
spec.Output[param.Rename] = param.ExtractType spec.Output[param.Rename] = param.ExtractType
} }
@ -37,28 +32,20 @@ func makeSpec(service config.Service) spec {
} }
// checks for HandlerFn input arguments // checks for HandlerFn input arguments
func (s spec) checkInput(fnt reflect.Type) error { func (s spec) checkInput(fnv reflect.Value) error {
if fnt.NumIn() != 1 { fnt := fnv.Type()
return ErrMissingHandlerArgument
}
// arg[0] must be api.Request
requestArg := fnt.In(0)
if !requestArg.AssignableTo(reflect.TypeOf(api.Request{})) {
return ErrMissingRequestArgument
}
// no input -> ok // no input -> ok
if len(s.Input) == 0 { if len(s.Input) == 0 {
return nil return nil
} }
if fnt.NumIn() < 2 { if fnt.NumIn() != 1 {
return ErrMissingHandlerArgumentParam return ErrMissingHandlerArgumentParam
} }
// arg[1] must be a struct // arg must be a struct
structArg := fnt.In(1) structArg := fnt.In(0)
if structArg.Kind() != reflect.Struct { if structArg.Kind() != reflect.Struct {
return ErrMissingParamArgument return ErrMissingParamArgument
} }
@ -71,7 +58,7 @@ func (s spec) checkInput(fnt reflect.Type) error {
} }
if !ptype.AssignableTo(field.Type) { if !ptype.AssignableTo(field.Type) {
return fmt.Errorf("%s: %w (%s)", name, ErrWrongParamTypeFromConfig, ptype) return fmt.Errorf("%s: %w (%s instead of %s)", name, ErrWrongParamTypeFromConfig, field.Type, ptype)
} }
} }
@ -79,7 +66,8 @@ func (s spec) checkInput(fnt reflect.Type) error {
} }
// checks for HandlerFn output arguments // checks for HandlerFn output arguments
func (s spec) checkOutput(fnt reflect.Type) error { func (s spec) checkOutput(fnv reflect.Value) error {
fnt := fnv.Type()
if fnt.NumOut() < 1 { if fnt.NumOut() < 1 {
return ErrMissingHandlerOutput return ErrMissingHandlerOutput
} }
@ -99,10 +87,15 @@ func (s spec) checkOutput(fnt reflect.Type) error {
return ErrMissingParamOutput return ErrMissingParamOutput
} }
// fail if first output is not a struct // fail if first output is not a pointer to struct
structOutput := fnt.Out(0) structOutputPtr := fnt.Out(0)
if structOutputPtr.Kind() != reflect.Ptr {
return ErrMissingParamOutput
}
structOutput := structOutputPtr.Elem()
if structOutput.Kind() != reflect.Struct { if structOutput.Kind() != reflect.Struct {
return ErrMissingParamArgument return ErrMissingParamOutput
} }
// fail on invalid output // fail on invalid output
@ -112,8 +105,13 @@ func (s spec) checkOutput(fnt reflect.Type) error {
return fmt.Errorf("%s: %w", name, ErrMissingOutputFromConfig) return fmt.Errorf("%s: %w", name, ErrMissingOutputFromConfig)
} }
if !ptype.AssignableTo(field.Type) { // ignore types evalutating to nil
return fmt.Errorf("%s: %w (%s)", name, ErrWrongOutputTypeFromConfig, ptype) if ptype == nil {
continue
}
if !ptype.ConvertibleTo(field.Type) {
return fmt.Errorf("%s: %w (%s instead of %s)", name, ErrWrongOutputTypeFromConfig, field.Type, ptype)
} }
} }