共计 19073 个字符,预计需要花费 48 分钟才能阅读完成。
对话零碎
目前网易云信 IM SDK 反对全平台,IM SDK 每次发版除了要针对新性能进行测试外,回归测试也占了很大比重,随着 IM SDK 反对的平台越来越多,API 接口越来越丰盛,每次须要回归测试的范畴也在一直减少,如果以人工来实现,不仅效率低下,而且可能会产生漏测、误判等问题。为了进步回归效率、继续监控程序品质,云信在“品质保障体系”建设中投入了大量精力来搭建和欠缺“自动化测试平台”,“API 自动化测试”作为自动化测试平台的重要组件,为“单元测试”、“回归测试”、“每日构建”等性能提供了无力撑持,接入自动化测试平台十分有意义,其中“API 自动化测试”的次要流程如下图所示,本文就“依据 API 信息转为 API 调用”、“API 调用后果转换为用例执行后果数据”在桌面端(Windows、MacOSX、Linux)实现的关键技术进行探讨。
为什么要应用反射
下图所展现的内容是由“自动化测试平台”下发的用例信息(为了不便展现,字段有裁剪)。
其中 className、methodName 形容了 SDK-API 的 NIMAuthService::login 办法,params 结点则形容了 login 办法的参数, 依据上节提到的“依据 API 信息转为 API 调用”、“API 调用后果转换为用例执行后果数据”,其中波及到转换有两种:
- API 转换
依据用例传入的 className、methodName 来找到对应的 API。
从上图的数据上来看要找到 NIMAuthService::login 进行调用。
- 数据转换
JsonData2cppStruct,测试平台 Json 数据转换到 C++ 数据。
cppStruct2JsonData,C++ 执行后果数据转换为 Json 数据上报到测试平台。
从上图的数据上来看要把 data 节点的数据转换为 NIMAuthInfo,做为 NIMAuthService::login 的调用参数.
对于这种场景应用“反射”无疑是最合适与优雅的形式,为什么这么说呢,能够进行如下的比照:
IM SDK-API(仅示例):
namespace nim {
struct AuthInfo {
std::string accid;
std::string token;
std::string appkey;
};
struct AuthCallbackParam {
std::string accid;
bool result;
int step;
};
using LoginCallback = std::function<void(const AuthCallbackParam&)>;
struct NIMAuthService {static bool login(const AuthInfo& info) {
std::cout << info.accid << "/" << info.token << "/" << info.appkey << std::endl;
return true;
}
static void loginEx(const AuthInfo& info,const LoginCallback& callback) {
std::cout << info.accid << "/" << info.token << "/" << info.appkey << std::endl;
if (callback != nullptr) {callback(true);
}
}
};
}
不应用反射机制,须要依据输出的 Json 数据来查找对应的 service,并找到对应的办法,把 Json 数据转换为对应的 C++ 构造,而后进行 API 调用,基中会波及到大量的反复逻辑的判断。
struct ParameterConverter {static bool FromJsonData(const std::string& param, nim::AuthInfo& auth_info) {auto json_param = nlohmann::json::parse(param);
if (json_param.is_object()) {if(auto it = json_param.find("accid");it != json_param.end())
it->get_to(auth_info.accid);
if (auto it = json_param.find("token"); it != json_param.end())
it->get_to(auth_info.token);
if (auto it = json_param.find("appkey"); it != json_param.end())
it->get_to(auth_info.appkey);
}
return true;
}
};
bool InvokeSDKAPI(
const std::string& serviceName,
const std::string& functionName,
const std::string param) {if (serviceName.compare("NIMAuthService") == 0) {if (functionName.compare("login") == 0) {
nim::AuthInfo auth_info;
if (ParameterConverter::FromJsonData(param, auth_info)) {nim::NIMAuthService::login(auth_info);
}
}
......
}
......
return true;
}
引入反射机制之后:
RefRegStruct(nim::AuthInfo
, RefRegStructField(accid, "accid")
, RefRegStructField(token, "token")
, RefRegStructField(appkey, "appkey")
);
bool InvokeSDKAPI(
const std::string& serviceName,
const std::string& functionName,
const std::string param) {auto res = SDKAPIRefManager::InvokeAPI({ serviceName, functionName}, {param});
std::cout << serviceName << "::" << functionName << "result:" << res << std::endl;
return true;
}
int main() {std::string param("{\"accid\":\"test_accid\",\"token\":\"123456\",\"appkey\":\"45c6af3c98409b18a84451215d0bdd6e\"}");
auth_info.accid = "test_accid";
RegApi("NIMAuthService", "login",&nim::NIMAuthService::login);
auto res = SDKAPIRefManager::InvokeAPI({"NIMAuthService", "login"}, {param});
std::cout << "NIMAuthService::login result:" << res << std::endl;
return 0;
}
在进行 API 调用时,能够间接从 SDKAPIRefManager 中查问已注册的反射信息进行 API 调用。
引入反射的意义 :
对于对立、通用的接口形容,应用反射来实现从无类型数据结构运行时参数,查找 API 并实现调用。
- 测试平台,实现 SDK 平台无关性、业务一致性测试。
- 测试接入程序具备“接入便捷”,“兼容多版本 SDK”、“易保护”的个性。
配合代码的自动化生成,在不投入过多精力的状况下,也能够做到程序品质的无效晋升。对于“API 反射信息注册”会在上面的章节中进行具体的介绍。
反射的实现原理
对于 Java、Object-c、C# 的程序员来说”反射“是很罕用的机制,但 C++ 出于性能及语言个性的思考,并没有实现反射机制,C++ 尽管没有提供反射的实现,但弱小的 C++ 语言能够很不便的实现反射机制。
为了不便进行转换应用的 Json 库为 nlohmann/json(https://github.com/nlohmann/json)
能够先看上面示例代码:
struct Test {int index{1};
std::string name{"test_1"};
void printInfo() const {std::cout << "index:" << index << "name:" << name << std::endl;}
};
int main() {
auto index_addr = &Test::index;
auto name_addr = &Test::name;
auto fun_print_addr = &Test::printInfo;
Test test;
test.index = 1;
test.name = "test_1";
test.printInfo();
test.*index_addr = 2;
test.*name_addr = "test_2";
(test.*fun_print_addr)();
return 0;int index{0};
}
输入后果:
index:1 name:test_1
index:2 name:test_2
察看示例中的代码能够发现,通过获得类成员变量的地址来实现对具体类实例对象成员进行调用。
auto index_addr = &Test::index;
auto name_addr = &Test::name;
auto fun_print_addr = &Test::printInfo;
test.*index_addr = 2;
test.*name_addr = “test_2”;
(test.*fun_print_addr)();
而这种机制,也是咱们实现 C++ 反射的基石。
反射的实现次要能够分为三个方面:
- 元数据生成
元数据是 C++ 对象的形容信息,保留了 C++ 全局函数、全局对象、对象的成员函数及成员变量的地址、用于反射查找的名称等信息,参考示例代码,能够把 auto index_addr、auto name_addr、auto fun_print_addr 了解为元数据。
- 元数据反射
元数据反射能够了解为根据内部输出的反射信息包含对象名称、数据的格式化字符串(比方 Json、xml 等 )、办法名称、生成对应的 C++ 对象或查找对应的 C++ 办法。
- API 调用
依据查找到办法及依据数据的格式化字符串生成的 C++ 对象来实现相应的 API 调用。
依照元数据生成的机会, 反射能够分为两种类型:
- 动静反射
在程序运行时才会生成对应类型的元数据信息,长处是须要转换时才生成元数据信息,占用内存小,毛病是运行速度稍慢。
- 动态反射
在程序的编译期生成元数据信息,长处是元数据不须要动静生成,运行速度稍快,毛病是包体积与内存占用会减少。
对于自动化测试这个场景,在外部应用,对包大小、内存占用等没有太大要求,为了进步用例执行效率以及反射代码的主动生成的便捷性,咱们采纳了动态反射。
动态反射的实现
1. 构造体的元数据信息保留
如下面的示例代码,struct Test 的每个字段都能够获得其在类中的偏移 & 类型信息,把偏移 & 类型信息及变量名称保留下来,生成字段的反射信息,struct 做为反射信息查找的关键字。
C++11 提供了 std::tuple,能够很不便的存储这些信息,形如:
std::tuple
< std::tuple<TSField1,std::string/*field1_name*/>
, std::tuple<TSField2,std::string/*field2_name*/>
, std::tuple<TSField3,std::string/*field3_name*/>
,......>;
通过特化 nlohmann::adl_serializer 联合 struct 的元数据信息来实现 struct 的 to_json、from_json。
对于这部分内容已有大神给出了比拟具体的介绍《古代 C++ 编译时 构造体字段反射》
(https://zhuanlan.zhihu.com/p/…)
2. API 的元数据信息保留
从”测试服务器“上下发的测试用例,SDK-API 参数以 Json 的格局传递,因为把握了构造体与 Json 的数据的转换,如果把 API 的调用转换为字符串映射,即对 SDK-API 进行类型擦除,在进行 API 调用时依据 SDK-API 注册信息,把 jsonArray/stringArray 转换 tuple 构造(例如 std::tuple),而后进行 tupleCall,实现 API 的调用。
映射关系如下:
所以有如下的定义:
/// 参数类型定义
using function_param_t = std::string;
/// 返回值定义
using function_return_t = std::string;
/// sdk-api 名称定义, 用于反射信息注册与查找
struct _function_name {
std::string service_name;
std::string api_name;
};
using function_name_t = _function_name;
using function_param_list_t = std::vector<function_param_t>;
/// 类型擦除后的 api 格局定义
using api_function_wrapper_t = std::function<function_return_t(const function_param_list_t&)>;
/// api 反射信息的聚合包装
struct _api_wrapper {
function_name_t function_name;
api_function_wrapper_t api;
};
using api_wrapper_t = _api_wrapper;
/// api 反射信息的存储容器定义
using api_function_list_t = std::map<function_name_t, api_wrapper_t>;
3. API 类型擦除
限于篇幅,只介绍一下动态函数的解决,所谓 API 类型擦除是指把不同类型的 API 接口对立到一样的定义形式,这里咱们指定的对立定义 using api_function_wrapper_t = std::function<function_return_t(const function_param_list_t&)>;
function_return_t:API 返回值的 Json 格局字符串。
function_param_list_t:API 参数的 Json 格局字符串。
JsonParam2Tuple 办法就是依据“构造体反射信息”,把测试平台下发的 Json 参数映射为 tuple 构造,例如 std::tuple。
MakeInvokeApi 用于生成类型擦除的通用 API 调用对象
(api_function_wrapper_t)
_InvokeStaticApi 能够了解为 SDK-API 的真正调用,它保留了 SDK-API 的原始形容,并负责把 JsonParams 转换为 tuple,调用 tupleCall,获得返回值后把 cppParam 转换为 JsonParam。
/// 注册 api 反射信息
template <typename TApi, TApi api,
typename std::enable_if_t<!std::is_member_function_pointer<TApi>::value,nullptr_t> = nullptr>
static void RegisterApi(const std::string& service_name,const std::string& api_name) {
api_wrapper_t _api_wrapper;
_api_wrapper.function_name = {service_name,api_name};
_api_wrapper.api = MakeInvokeApi<TApi>(api);
RegisterApi(_api_wrapper);
}
static void RegisterApi(const api_wrapper_t& api) {api_list[api.function_name] = api;
}
/// 生成类型擦除后的 api 元信息
template <typename TApi,
typename std::enable_if_t<!std::is_member_function_pointer<TApi>::value,nullptr_t> = nullptr>
static api_function_wrapper_t MakeInvokeApi(TApi api) {return [api](const function_param_list_t& info) -> function_return_t {return _InvokeStaticApi(info, api);
};
}
template <typename R, typename... Args,typename std::enable_if<!std::is_void<R>::value, nullptr_t>::type = nullptr>
static function_return_t _InvokeStaticApi(const function_param_list_t& info,R(*f)(Args...)) {auto _tuple = JsonParam2Tuple<Args...>(info);
auto _value = TupleCall(f, _tuple);
return StructToJson(_value);
}
template<typename TStruct>
static TStruct JsonToStruct(const std::string& json_param) {auto json_obj = nlohmann::json::parse(json_param);
if (json_obj.is_object())
return json_obj.get<TStruct>();
return TStruct();}
template<typename TStruct>
static std::string StructToJson(const TStruct& param) {return nlohmann::json(param).dump();}
template <typename... Args, std::size_t... Is>
static auto JsonParam2TupleImpl(const function_param_list_t& info,
const std::index_sequence<Is...>) {return std::make_tuple(JsonToStruct<std::decay<Args>::type>(info[Is])...);
}
template <typename... Args>
static auto JsonParam2TupleImpl(const function_param_list_t& info) {return JsonParam2TupleImpl<Args...>(info, std::make_index_sequence<sizeof...(Args)>());
}
template <typename... TArgs>
static auto JsonParam2Tuple(const function_param_list_t& info) {return JsonParam2TupleImpl<TArgs...>(info);
}
template <typename TReturn, typename... TArgs, std::size_t... I>
static TReturn TupleCallImpl(TReturn(*fun)(TArgs...),
const std::tuple<std::remove_reference_t<std::remove_cv_t<TArgs>>...>& tup,
std::index_sequence<I...>) {return fun(std::get<I>(tup)...);
}
template <typename TReturn, typename... TArgs,
typename = typename std::enable_if<!std::is_member_function_pointer<
TReturn(*)(TArgs...)>::value>::type>
static TReturn TupleCall(TReturn(*fun)(TArgs...),
const std::tuple<std::remove_reference_t<std::remove_cv_t<TArgs>>...>& tup) {return TupleCallImpl(fun, tup,std::make_index_sequence<sizeof...(TArgs)>());
}
4. API 调用
通过注册信息来调用相应的 API,通过 api_name 来找到已注册的元数据信息,取到 api_wrapper,进行 API 调用,与一般函数调用无异。
static function_return_t InvokeAPI(
const function_name_t& api_name,
const function_param_list_t& param_list) {auto it = api_list.find(api_name);
if (it != api_list.end())
return it->second.api(param_list);
return function_return_t();}
5. 对于回调的解决
在 SDK 的模仿代码中有 NIMAuthService::loginEx 的定义,基参数列表是一个 LoginCallback,它是一个函数对象,无奈通过 Json 来进行格式化,此时 loginEx 转换的 param_list[1](LoginCallback)仅相当于一个占位符,它的类型,能够在注册 API 时进行推导,而后通过 JsonToStruct 的特化来进行解决,生成能够与 SDK-API 兼容的回调对象,在生成的回调对象执行中,把回调后果告诉进来。
特化及示例代码:
RefRegStruct(nim::AuthCallbackParam
, RefRegStructField(accid, "accid")
, RefRegStructField(step, "step")
, RefRegStructField(result, "result")
);
template <>
nim::LoginCallback SDKAPIRefManager::JsonToStruct<nim::LoginCallback>(const function_param_t& value)
{
return SDKAPIRefManager::MakeAPICallback(
"nim::LoginCallback",
((nim::LoginCallback*)(nullptr))
);
}
int main() {std::string param("{\"accid\":\"test_accid\",\"token\":\"123456\",\"appkey\":\"45c6af3c98409b18a84451215d0bdd6e\"}");
auth_info.accid = "test_accid";RegApi("NIMAuthService", "loginEx",nim::NIMAuthService::loginEx);
SDKAPIRefManager::InvokeAPI({"NIMAuthService", "loginEx"}, {struct_text ,"LoginCallback"});
return 0;
}
RegApi("NIMAuthService", "login",&nim::NIMAuthService::login);
对应的实现代码:
template <typename TTup, typename... Args, size_t... Is>
static void TupleToCbArgsImpl(const TTup& tup,
function_param_list_t& args,
std::index_sequence<Is...>) {args = { StructToJson(std::get<Is>(tup))... };
}
template <typename TTup, typename... Args>
static void TupleToCbArgsImpl(const TTup& tup,function_param_list_t& args) {
TupleToCbArgsImpl<TTup, Args...>(tup, args, std::make_index_sequence<sizeof...(Args)>());
}
template <typename... TArgs>
static function_param_list_t TupleToCbArgs(const std::tuple<TArgs...>& tup) {
function_param_list_t args;
TupleToCbArgsImpl<std::tuple<TArgs...>, TArgs...>(tup, args);
return args;
}
template <typename TReturn, typename... TArgs>
static std::function<TReturn(TArgs...)>
MakeAPICallback(
const std::string& callback_name,
const std::function<TReturn(TArgs...)>* realcb) {auto callback = [callback_name](TArgs... param) -> TReturn {auto tup = std::make_tuple(std::forward<TArgs>(param)...);
auto ret = TupleToCbArgs(tup);
OnAPICallback(callback_name,ret);
return TReturn();};
return callback;
}
static void OnAPICallback(const std::string& callback_name, const function_param_list_t& param) {
std::cout << callback_name << std::endl;
std::cout << "params:" << std::endl;
std::cout << "--begin" << std::endl;
for (auto& it : param) {std::cout << it << std::endl;}
std::cout << "--end" << std::endl;
}
回调的注册能够应用宏来实现:
#define CallbackDescription(Callback) ((Callback *)(nullptr))
#define CallbackSpecialization(callback_name,Callback) \
template <> \
Callback SDKAPIRefManager::JsonToStruct<Callback>( \
const function_param_t &value) { \
return SDKAPIRefManager::MakeAPICallback(callback_name, \
CallbackDescription(Callback)); \
}
示例代码总览:
namespace nim {
struct AuthInfo {
std::string accid;
std::string token;
std::string appkey;
};
struct AuthCallbackParam {
std::string accid;
bool result;
int step;
};
using LoginCallback = std::function<void(const AuthCallbackParam& param)>;
struct NIMAuthService {static bool login(const AuthInfo& info) {
std::cout
<< "api-NIMAuthService::login"
<< info.accid << "/"
<< info.token << "/"
<< info.appkey << std::endl;
return true;
}
static void loginEx(const AuthInfo& info,const LoginCallback& callback) {
std::cout
<< "api-NIMAuthService::loginEx"
<< info.accid << "/"
<< info.token << "/"
<< info.appkey << std::endl;
if (callback != nullptr) {
AuthCallbackParam param;
callback({info.accid,true,3});
}
}
};
}
RefRegStruct(nim::AuthInfo
, RefRegStructField(accid, "accid")
, RefRegStructField(token, "token")
, RefRegStructField(appkey, "appkey")
);
RefRegStruct(nim::AuthCallbackParam
, RefRegStructField(accid, "accid")
, RefRegStructField(step, "step")
, RefRegStructField(result, "result")
);
CallbackSpecialization("nim::LoginCallback", nim::LoginCallback);
int main() {std::string param("{\"accid\":\"test_accid\",\"token\":\"123456\",\"appkey\":\"45c6af3c98409b18a84451215d0bdd6e\"}");
RegApi("NIMAuthService", "login",&nim::NIMAuthService::login);
RegApi("NIMAuthService", "loginEx", &nim::NIMAuthService::loginEx);
auto res = SDKAPIRefManager::InvokeAPI({"NIMAuthService", "login"}, {param});
std::cout << "NIMAuthService::login result:" << res << std::endl;
SDKAPIRefManager::InvokeAPI({"NIMAuthService", "loginEx"}, {param ,"LoginCallback"});
return 0;
}NIMAuthService::login result:true
输入后果:
api-NIMAuthService::logintest_accid / 123456 / 45c6af3c98409b18a84451215d0bdd6e
NIMAuthService::login result:true
api-NIMAuthService::loginExtest_accid / 123456 / 45c6af3c98409b18a84451215d0bdd6e
nim::LoginCallback
params:
--begin
{"accid":"test_accid","result":true,"step":3}
--end
C++ 动态反射在 IM SDK 开发中的利用
反射信息的生成
IM SDK 接提供了大量接口,在生成反射信息时,如果依附人肉手撸代码的形式会存在以下几个问题:
- 工作量微小
- 容易出错
- API 改变,须要搜寻出对应的反射信息,一一进行批改
所以咱们引入了 libclang 来主动生成反射代码,基于 clang 的源到源转译工具能够参考咱们的一篇分享文章《NeCodeGen:基于 clang 的源到源转译工具》。
主动生成的代码如下所示,节选局部代码片段, 其中 nim_api::NIM_AuthInfo 为云信 IM SDK(elite 版)中对于登录服务的封装,NIMAuthService 是反射信息的注册器。
/**
* @brief
* 登录参数定义
*/
ReflectionDefinitionAndReg(nim_api_NIM_AuthInfo,nim_api::NIM_AuthInfo,
/// 利用的 appkey
appKey, "appKey",
/// 登录账号
accid, "accid",
/// 登录明码
token, "token"
);
/**
* @brief
* 登录后果回调参数定义
*/
ReflectionDefinitionAndReg(nim_api_NIM_LoginCBParam,nim_api::NIM_LoginCBParam,
/// 错误码
code, "code",
/// 以后登录步骤
step, "step",
/// 以后登录步骤形容
message, "message",
/// 是否为从新登录
relogin, "relogin"
);
NE_GTAPI_CALLER::ApiObject NIMAuthService::Init(NE_GTAPI_CALLER::ApiEnv env, NE_GTAPI_CALLER::ApiObject exports) {return InternalInit(TransClassName(NIMAuthService), env, exports, {RegApi("registerKickout",&nim_api::AuthService::registerKickout),
RegApi("registerMultiSpot",&nim_api::AuthService::registerMultiSpot),
RegApi("registerDisconnect",&nim_api::AuthService::registerDisconnect),
RegApi("registerRelogin",&nim_api::AuthService::registerRelogin),
RegApi("login",&nim_api::AuthService::login),
RegApi("logout",&nim_api::AuthService::logout),
RegApi("kickOtherClient",&nim_api::AuthService::kickOtherClient),
RegApi("getLoginState",&nim_api::AuthService::getLoginState),
});
}
void NIMAuthService::RegisterNotifyCallback() {RegisterSDKNotifyCallback("onKickout", GetSDKService(), &nim_api::AuthService::registerKickout);
RegisterSDKNotifyCallback("onMultiSpot", GetSDKService(), &nim_api::AuthService::registerMultiSpot);
RegisterSDKNotifyCallback("onDisconnect", GetSDKService(), &nim_api::AuthService::registerDisconnect);
RegisterSDKNotifyCallback("onRelogin", GetSDKService(), &nim_api::AuthService::registerRelogin);
}
利用场景
- 自动化测试
后面几个章节在介绍 C++ 反射实现时特定了“API 自动化的场景”,起初引入 C++ 反射也是为了实现自动化测试的接入,目前应用反射机制封装的 C++ 自动化测试接入 SDK 已实现了桌面端的笼罩,并达到了与 Java/object-c 同样的成果,测试团队只须要编写一份测试用例,就能够对目前 SDK 进行测试。
- electron-sdk 封装
云信 IM sdk N-Api 实现 node addon 的形式接入 native IM sdk,Napi::CallbackInfo 能够很简略的转换为 Json 格局数据,通过反射的形式来调用底层 native sdk 再适合不过了。
ts 代码节选
export interface NIM_AuthInfo {
appKey: string,
accountId: string,
token: string
}
export interface NIM_LoginCBParam {
code: number,
step: number,
message: string,
relogin: boolean
}
export interface AuthenticationAPI {getParamRefValue(paramName:string) : string
login(authInfo: NIM_AuthInfo, callback: LoginCallback): void
logout(callback: LogoutCallback): void;
kickOtherClient(param: NIM_KickOtherClientRequestParam, callback: KickOtherClientCallback): void
getLoginState(): NIM_LoginState}
class Authentication extends ServiceBase {constructor(clientInstance: ClientInstance) {super()
this._authService = new ServiceBase.nim.Authentication(clientInstance, this.emit.bind(this))
}
getParamRefValue(paramName:string) : string{return this._authService.getParamRefValue(paramName)
}
login(authInfo: nim_api.NIM_AuthInfo, callback: nim_api.LoginCallback): void {this._authService.login(authInfo, callback)
}
logout(callback: nim_api.LogoutCallback): void {this._authService.logout(callback)
}
kickOtherClient(param: nim_api.NIM_KickOtherClientRequestParam, callback: nim_api.KickOtherClientCallback): void {this._authService.kickOtherClient(param, callback)
}
getLoginState(): nim_api.NIM_LoginState {return this._authService.getLoginState()
}
private _authService: nim_api.AuthenticationAPI
}
js 代码节选
funList: {enter: {moduleName: '_chatroomService', funName: 'enter', funParam: ['NIM_ChatroomEnterParam'], cbCount: 1},
independentEnter: {moduleName: '_chatroomService', funName: 'independentEnter', funParam: ['NIM_ChatroomIndependentEnterParam'], cbCount: 1},
anoymityEnter: {moduleName: '_chatroomService', funName: 'anoymityEnter', funParam: ['NIM_ChatroomAnoymityEnterParam'], cbCount: 1},
exit: {moduleName: '_chatroomService', funName: 'exit', funParam: [], cbCount: 1},
getInfo: {moduleName: '_chatroomService', funName: 'getInfo', funParam: [], cbCount: 1},
updateInfo: {moduleName: '_chatroomService', funName: 'updateInfo', funParam: ['NIM_ChatroomInfoUpdateParam'], cbCount: 1}
}
invokApi (moduleName, funName) {var _funParam = this.activeFunList[funName].funParam
var _cbCount = this.activeFunList[funName].cbCount
var _callback = (requestAck) => {if (!requestAck && requestAck !== 0) {console.log('\t🔹 Callback value:\n\t\t -- Null or Undefined')
} else {console.log('\t🔹 Callback value:\n\t\t', requestAck)
}
}
var _callback2 = (requestAck) => {if (!requestAck && requestAck !== 0) {console.log('\t🔹 Callback value:\n\t\t -- Null or Undefined')
} else {console.log('\t🔹 Callback value:\n\t\t', requestAck)
}
}
var _result
if (_funParam.length === 0) {console.log('🔷 -- API【', moduleName, '/', funName, '】\n\t🔹 callbackCount:', _cbCount, '\n\t🔹 Param:\n\t\t', 'No parameters required')
if (_cbCount === 0) {_result = this.clientInstance[moduleName][funName]()} else if (_cbCount === 1) {_result = this.clientInstance[moduleName][funName](_callback1)
} else if (_cbCount === 2) {_result = this.clientInstance[moduleName][funName](_callback1, _callback2)
} else {}} else if (_funParam.length === 1) {var paramOne = JSON.parse(this.requestParam1)
console.log('🔷 -- API【', moduleName, '/', funName, '】\n\t🔹 callbackCount:', _cbCount, '\n\t🔹 Param:\n\t\t', paramOne)
if (_cbCount === 0) {_result = this.clientInstance[moduleName][funName](paramOne)
} else if (_cbCount === 1) {_result = this.clientInstance[moduleName][funName](paramOne, _callback1)
} else if (_cbCount === 2) {_result = this.clientInstance[moduleName][funName](paramOne, _callback1, _callback2)
} else {}} else if (_funParam.length === 2) {var paramTwoOne = JSON.parse(this.requestParam1)
var paramTwoTwo = JSON.parse(this.requestParam2)
console.log('🔷 -- API【', moduleName, '/', funName, '】\n\t🔹 callbackCount:', _cbCount, '\n\t🔹 Param1:\n\t\t', paramTwoOne, '\n\t🔹 Param2:\n\t\t', paramTwoTwo)
if (_cbCount === 0) {_result = this.clientInstance[moduleName][funName](paramTwoOne, paramTwoTwo)
} else if (_cbCount === 1) {_result = this.clientInstance[moduleName][funName](paramTwoOne, paramTwoTwo, _callback1)
} else if (_cbCount === 2) {_result = this.clientInstance[moduleName][funName](paramTwoOne, paramTwoTwo, _callback1, _callback2)
} else {}} else { }
if (!_result && _result !== 0) {console.log('\t🔹 Return value:\n\t\t -- Null or Undefined')
} else {console.log('\t🔹 Return value:\n\t\t', JSON.stringify(_result))
}
}