From 8b5973327c437fb84a6e06d3aad692e34eeaf674 Mon Sep 17 00:00:00 2001 From: Raphael Pinto Date: Wed, 30 Mar 2022 10:21:14 -0300 Subject: [PATCH] feat: add error handling --- config/config.go | 10 ++++++ services/rulles.go | 8 +++-- types/context.go | 29 ++++++++++++---- types/context_test.go | 77 ++++++++++++++++++++++++++++++++----------- types/typedMap.go | 7 +++- 5 files changed, 103 insertions(+), 28 deletions(-) diff --git a/config/config.go b/config/config.go index 4797461..75353ac 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "net/http" "os" "strings" @@ -26,6 +27,8 @@ type Config struct { var config = &Config{} +var loaded = false + //LoadConfig ... func LoadConfig() (err error) { viper.AddConfigPath("./") @@ -76,5 +79,12 @@ func LoadConfig() (err error) { //GetConfig ... func GetConfig() *Config { + if !loaded { + err := LoadConfig() + loaded = true + if err != nil { + panic(fmt.Sprintf("load config error: %s", err)) + } + } return config } diff --git a/services/rulles.go b/services/rulles.go index 0254868..c8a5b07 100644 --- a/services/rulles.go +++ b/services/rulles.go @@ -125,8 +125,12 @@ func (s Eval) Eval(ctx *types.Context, knowledgeBase *ast.KnowledgeBase) (*types return nil, err } - log.Print("Context:\n\t", ctx.GetEntries(), "\n\n") - log.Print("Features:\n\t", result.GetFeatures(), "\n\n") + if ctx.Has("errors") && len(ctx.GetMap("errors").GetEntries()) > 0 { + result.Put("errors", ctx.GetMap("errors").GetEntries()) + } + + log.Debug("Context:\n\t", ctx.GetEntries(), "\n\n") + log.Debug("Features:\n\t", result.GetFeatures(), "\n\n") evalMutex.Unlock() diff --git a/types/context.go b/types/context.go index a8f753d..8ff3aec 100644 --- a/types/context.go +++ b/types/context.go @@ -89,6 +89,16 @@ func (c *Context) load(param string) interface{} { } func (c *Context) loadImpl(param string) interface{} { + defer func() { + r := recover() + if r == nil { + return + } + if !c.Has("errors") { + c.Put("errors", NewTypedMap()) + } + c.GetMap("errors").AddItem(param, r) + }() remote, ok := c.RemoteLoadeds[param] if !ok { panic("The param it's not registry as remote loaded") @@ -116,14 +126,15 @@ func (c *Context) GetEntry(param string) interface{} { } type resolveInputV1 struct { - Resolver string `json:"resolver"` - Context map[string]interface{} `json:"context"` - Load []string `json:"load"` + Resolver string `json:"resolver,omitempty"` + Context map[string]interface{} `json:"context,omitempty"` + Load []string `json:"load,omitempty"` } type resolveOutputV1 struct { - Context map[string]interface{} `json:"context"` - Errors map[string]interface{} `json:"errors"` + Context map[string]interface{} `json:"context,omitempty"` + Errors map[string]interface{} `json:"errors,omitempty"` + Error string `json:"error,omitempty"` } func (c *Context) resolve(resolver string, param string) interface{} { @@ -155,6 +166,8 @@ func (c *Context) resolveImpl(resolver string, param string) interface{} { panic("error on encode input") } + log.Debugf("Resolving with '%s' decoded: %v", url, buf.String()) + req, err := http.NewRequest("POST", url, &buf) if err != nil { panic("error on create Request") @@ -173,7 +186,7 @@ func (c *Context) resolveImpl(resolver string, param string) interface{} { panic("error on read the body") } - log.Infof("Resolving with '%s': %v > %s", url, input, string(data)) + log.Infof("Resolved with '%s': %v > %s", url, input, string(data)) output := resolveOutputV1{} err = json.Unmarshal(data, &output) @@ -185,5 +198,9 @@ func (c *Context) resolveImpl(resolver string, param string) interface{} { panic(fmt.Sprintf("%s", output.Errors)) } + if output.Error != "" { + panic(output.Error) + } + return output.Context[param] } diff --git a/types/context_test.go b/types/context_test.go index e7fc7e3..54ace96 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -106,16 +106,15 @@ func TestLoad(t *testing.T) { // TestLoadPanicNotRemoted start func TestLoadPanicNotRemoted(t *testing.T) { - defer func() { - r := recover() - if r != "The param it's not registry as remote loaded" { - t.Error("The panic message it's not throwed") - } - }() - ctx := NewContext() ctx.load("myRemoteParam") + got := ctx.GetMap("errors").GetSlice("myRemoteParam") + expected := []interface{}{"The param it's not registry as remote loaded"} + + if !reflect.DeepEqual(got, expected) { + t.Error("The error message it's not throwed") + } } // TestLoadPanicNotRemoted stop @@ -440,12 +439,6 @@ func (m *MockHTTPClientResponseDecodeMoreThenOneError) Do(req *http.Request) (*h m.t.Error("The loads dont match each other") } - output := resolveOutputV1{} - err = json.Unmarshal(data, &output) - if err != nil { - panic(err.Error()) - } - stringReader := strings.NewReader(`{ "errors": { "myparam": "myerror" @@ -512,12 +505,6 @@ func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { m.t.Error("The loads dont match each other") } - output := resolveOutputV1{} - err = json.Unmarshal(data, &output) - if err != nil { - panic(err.Error()) - } - stringReader := strings.NewReader(`{ "context": { "param_name": "myresult" @@ -546,3 +533,55 @@ func TestResolve(t *testing.T) { } // TestResolve stop + +// TestResolveUnexpectedError start +type MockHTTPClientUnexpectedError struct { + http.Client + t *testing.T +} + +func (m *MockHTTPClientUnexpectedError) Do(req *http.Request) (*http.Response, error) { + data, err := io.ReadAll(req.Body) + if err != nil { + panic(err.Error()) + } + + input := resolveInputV1{} + err = json.Unmarshal(data, &input) + if err != nil { + panic(err.Error()) + } + + if input.Resolver != "resolver_name" { + m.t.Error("the resolver is wrong") + } + + expectedLoad := []string{"param_name"} + + if !reflect.DeepEqual(input.Load, expectedLoad) { + m.t.Error("The loads dont match each other") + } + + stringReader := strings.NewReader(`{"error":"error message"}`) + + return &http.Response{ + Body: io.NopCloser(stringReader), + }, nil +} + +func TestResolveUnexpectedError(t *testing.T) { + defer func() { + r := recover() + if r != "error message" { + t.Error("The panic message it's not throwed") + } + }() + Client = &MockHTTPClientUnexpectedError{ + t: t, + } + ctx := NewContext() + + ctx.resolve("resolver_name", "param_name") +} + +// TestResolve stop diff --git a/types/typedMap.go b/types/typedMap.go index 04ae326..e40540d 100644 --- a/types/typedMap.go +++ b/types/typedMap.go @@ -126,10 +126,10 @@ func (c *TypedMap) GetBool(param string) bool { func (c *TypedMap) GetMap(param string) *TypedMap { value := c.Get(param) if value != nil { - result := NewTypedMap() v := reflect.ValueOf(value) kind := v.Kind() if kind == reflect.Map { + result := NewTypedMap() for _, key := range v.MapKeys() { strct := v.MapIndex(key) result.Put(key.String(), strct.Interface()) @@ -137,6 +137,11 @@ func (c *TypedMap) GetMap(param string) *TypedMap { return result } + tp, ok := value.(*TypedMap) + if ok { + return tp + } + panic("This param it's not a map") } return nil