From da1a9b344cf8c8cfd37248e8db2dda6c88771c27 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 20 Apr 2016 00:39:14 +0300 Subject: [PATCH] integration test for *cgr_rpc --- apier/v1/apier.go | 5 +++-- apier/v1/apier_local_test.go | 2 +- data/tariffplans/testtp/Actions.csv | 1 + engine/action.go | 20 ++++++++++++++++---- engine/actions_test.go | 2 +- general_tests/tp_it_test.go | 19 ++++++++++++++++++- utils/rpc_params.go | 4 ++-- utils/rpc_params_test.go | 22 +++++++++++++++++++--- utils/struct.go | 21 +++++++++++++++++++++ 9 files changed, 82 insertions(+), 14 deletions(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index b6abf47f0..e8eaa63ed 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -103,11 +103,12 @@ func (self *ApierV1) GetRatingPlan(rplnId string, reply *engine.RatingPlan) erro } func (self *ApierV1) ExecuteAction(attr *utils.AttrExecuteAction, reply *string) error { - accID := utils.AccountKey(attr.Tenant, attr.Account) at := &engine.ActionTiming{ ActionsID: attr.ActionsId, } - at.SetAccountIDs(utils.StringMap{accID: true}) + if attr.Tenant != "" && attr.Account != "" { + at.SetAccountIDs(utils.StringMap{utils.AccountKey(attr.Tenant, attr.Account): true}) + } if err := at.Execute(); err != nil { *reply = err.Error() return err diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 5c2504cec..124b6a984 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1297,7 +1297,7 @@ func TestApierResetDataAfterLoadFromFolder(t *testing.T) { if rcvStats.Destinations != 5 || rcvStats.RatingPlans != 5 || rcvStats.RatingProfiles != 5 || - rcvStats.Actions != 9 || + rcvStats.Actions != 10 || rcvStats.DerivedChargers != 3 { t.Errorf("Calling ApierV1.GetCacheStats received: %+v", rcvStats) } diff --git a/data/tariffplans/testtp/Actions.csv b/data/tariffplans/testtp/Actions.csv index 98a48be14..1525522f2 100644 --- a/data/tariffplans/testtp/Actions.csv +++ b/data/tariffplans/testtp/Actions.csv @@ -9,3 +9,4 @@ TOPUP_DATA_r,*topup,,,,*monetary,*out,,DATA_DEST,,,*unlimited,,5000000,10,false, TOPUP_DATA_r,*topup,,,,*data,*out,,DATA_DEST,datar,,*unlimited,,50000000000,10,false,false,10 TOPUP_VOICE,*topup,,,,*voice,*out,,GERMANY_MOBILE,,,*unlimited,,50000,10,false,false,10 TOPUP_NEG,*topup,,,,*voice,*out,,GERMANY;!GERMANY_MOBILE,*zero1m,,*unlimited,,100,10,false,false,10 +RPC,*cgr_rpc,"{""Address"": ""localhost:2013"",""Transport"":""*gob"",""Method"":""ApierV2.SetAccount"",""Attempts"":1,""Async"" :false,""Param"":{""Account"":""rpc"",""Tenant"":""cgrates.org""}}",,,,,,,,,,,,,,, diff --git a/engine/action.go b/engine/action.go index 35834d55b..84669ff1f 100644 --- a/engine/action.go +++ b/engine/action.go @@ -657,7 +657,7 @@ type RPCRequest struct { Method string Attempts int Async bool - Param string + Param map[string]interface{} } func cgrRPCAction(account *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { @@ -677,13 +677,25 @@ func cgrRPCAction(account *Account, sq *StatsQueueTriggered, a *Action, acs Acti } else { client = params.Object } + if client == nil { + return utils.ErrServerError + } in, out := params.InParam, params.OutParam - x := in - if err := json.Unmarshal([]byte(req.Param), &x); err != nil { + p, err := utils.FromMapStringInterfaceValue(req.Param, in) + if err != nil { return err } - return client.Call(req.Method, in, out) + if !req.Async { + err = client.Call(req.Method, p, out) + utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> result: %+v err: %v", out, err)) + return err + } + go func() { + err := client.Call(req.Method, p, out) + utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> result: %+v err: %v", out, err)) + }() + return nil } // Structure to store actions according to weight diff --git a/engine/actions_test.go b/engine/actions_test.go index 02e941e8f..f9297d25a 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -2212,7 +2212,7 @@ func TestCgrRpcAction(t *testing.T) { "Method": "TestRPCParameters.Hopa", "Attempts":1, "Async" :false, - "Param": "{\"Name\":\"n\", \"Surname\":\"s\", \"Age\":10.2}"}`, + "Param": {"Name":"n", "Surname":"s", "Age":10.2}}`, } if err := cgrRPCAction(nil, nil, a, nil); err != nil { t.Error("error executing cgr action: ", err) diff --git a/general_tests/tp_it_test.go b/general_tests/tp_it_test.go index cb66dca3e..8165ed19c 100644 --- a/general_tests/tp_it_test.go +++ b/general_tests/tp_it_test.go @@ -214,7 +214,7 @@ func TestTpZeroNegativeCost(t *testing.T) { } else if cc.GetDuration() != 20*time.Second { t.Errorf("Calling Responder.MaxDebit got callcost: %v", utils.ToIJSON(cc)) } - var acnt *engine.Account + var acnt engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1013"} if err := tpRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error("Got error on ApierV2.GetAccount: ", err.Error()) @@ -222,3 +222,20 @@ func TestTpZeroNegativeCost(t *testing.T) { t.Errorf("Calling ApierV2.GetAccount received: %s", utils.ToIJSON(acnt)) } } + +func TestTpExecuteActionCgrRpc(t *testing.T) { + if !*testIntegration { + return + } + var reply string + if err := tpRPC.Call("ApierV2.ExecuteAction", utils.AttrExecuteAction{ActionsId: "RPC"}, &reply); err != nil { + t.Error("Got error on ApierV2.ExecuteAction: ", err.Error()) + } else if reply != utils.OK { + t.Errorf("Calling ExecuteAction got reply: %s", reply) + } + var acnt engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "rpc"} + if err := tpRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error("Got error on ApierV2.GetAccount: ", err.Error()) + } +} diff --git a/utils/rpc_params.go b/utils/rpc_params.go index 9eaecba1a..69020fdec 100644 --- a/utils/rpc_params.go +++ b/utils/rpc_params.go @@ -10,7 +10,7 @@ var rpcParamsMap map[string]*RpcParams type RpcParams struct { Object rpcclient.RpcClientConnection - InParam interface{} + InParam reflect.Value OutParam interface{} } @@ -33,7 +33,7 @@ func RegisterRpcParams(name string, obj rpcclient.RpcClientConnection) { if methodType.NumIn() == 3 { // if it has three parameters (one is self and two are rpc params) rpcParamsMap[name+"."+method.Name] = &RpcParams{ Object: obj, - InParam: (reflect.New(methodType.In(1)).Elem()).Interface(), + InParam: reflect.New(methodType.In(1)), OutParam: reflect.New(methodType.In(2).Elem()).Interface(), } } diff --git a/utils/rpc_params_test.go b/utils/rpc_params_test.go index a500fd3c2..aac04ecdc 100644 --- a/utils/rpc_params_test.go +++ b/utils/rpc_params_test.go @@ -4,11 +4,17 @@ import "testing" type RpcStruct struct{} -func (rpc *RpcStruct) Hopa(normal string, out *float64) error { +type Attr struct { + Name string + Surname string + Age float64 +} + +func (rpc *RpcStruct) Hopa(normal Attr, out *float64) error { return nil } -func (rpc *RpcStruct) Tropa(pointer *string, out *float64) error { +func (rpc *RpcStruct) Tropa(pointer *Attr, out *float64) error { return nil } @@ -25,8 +31,18 @@ func TestRPCObjectPointer(t *testing.T) { if !found { t.Errorf("error getting rpcobject: %v (%+v)", rpcParamsMap, x) } - x, found = rpcParamsMap["RpcStruct.Tropa"] + a := x.InParam + if v, err := FromMapStringInterfaceValue(map[string]interface{}{"Name": "a", "Surname": "b", "Age": 10.2}, a); err != nil || v.(Attr).Name != "a" || v.(Attr).Surname != "b" || v.(Attr).Age != 10.2 { + t.Errorf("error converting to struct: %+v (%v)", v, err) + } + //TODO: make pointer in arguments usable + /*x, found = rpcParamsMap["RpcStruct.Tropa"] if !found { t.Errorf("error getting rpcobject: %v (%+v)", rpcParamsMap, x) } + b := x.InParam + log.Printf("T: %+v", b) + if v, err := FromMapStringInterfaceValue(map[string]interface{}{"Name": "a", "Surname": "b", "Age": 10.2}, b); err != nil || v.(Attr).Name != "a" || v.(Attr).Surname != "b" || v.(Attr).Age != 10.2 { + t.Errorf("error converting to struct: %+v (%v)", v, err) + }*/ } diff --git a/utils/struct.go b/utils/struct.go index 3da80ec84..e6f20fff3 100644 --- a/utils/struct.go +++ b/utils/struct.go @@ -194,6 +194,27 @@ func FromMapStringInterface(m map[string]interface{}, in interface{}) error { return nil } +func FromMapStringInterfaceValue(m map[string]interface{}, v reflect.Value) (interface{}, error) { + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + for fieldName, fieldValue := range m { + field := v.FieldByName(fieldName) + if field.IsValid() { + if !field.IsValid() || !field.CanSet() { + continue + } + structFieldType := field.Type() + val := reflect.ValueOf(fieldValue) + if structFieldType != val.Type() { + return nil, errors.New("Provided value type didn't match obj field type") + } + field.Set(val) + } + } + return v.Interface(), nil +} + // Update struct with map fields, returns not matching map keys, s is a struct to be updated func UpdateStructWithStrMap(s interface{}, m map[string]string) []string { notMatched := []string{}