Merge pull request #2934 from ZhouBox/main

restful: support querying nodes by multiple plugins
This commit is contained in:
fengzeroz
2026-08-21 09:57:04 +08:00
committed by GitHub
12 changed files with 111 additions and 31 deletions
+1 -1
View File
@@ -307,7 +307,7 @@ Neuron 将为 IIoT 平台提供一系列 API 服务,用于查询基本信息
**type** 必需
**plugin** 可选
**plugin** 可选,支持逗号分隔多个插件名,如 `plugin=Modbus TCP,Modbus RTU`
**node** 可选
+1 -1
View File
@@ -299,7 +299,7 @@ Neuron provide a series of API services for IIoT platform, to query the basic in
**type** required
**plugin** optional
**plugin** optional, support multiple plugin names separated by comma, e.g. `plugin=Modbus TCP,Modbus RTU`
**node** optional
+1
View File
@@ -40,6 +40,7 @@
#define NEU_DEFAULT_GROUP_INTERVAL 100
#define NEU_NODE_NAME_LEN 128
#define NEU_PLUGIN_NAME_LEN 32
#define NEU_PLUGIN_FILTER_MAX 16
#define NEU_NODE_TAGS_LEN 256
#define NEU_PLUGIN_LIBRARY_LEN 64
#define NEU_PLUGIN_DESCRIPTION_LEN 512
+2 -1
View File
@@ -556,7 +556,8 @@ typedef struct neu_req_del_node {
typedef struct neu_req_get_node {
neu_node_type_e type;
char plugin[NEU_PLUGIN_NAME_LEN];
int n_plugin;
char plugin[NEU_PLUGIN_FILTER_MAX][NEU_PLUGIN_NAME_LEN];
char node[NEU_NODE_NAME_LEN];
struct {
+38 -7
View File
@@ -203,10 +203,10 @@ void handle_del_adapter(nng_aio *aio)
void handle_get_adapter(nng_aio *aio)
{
int ret = 0;
char plugin_name[NEU_PLUGIN_NAME_LEN] = { 0 };
char node_name[NEU_NODE_NAME_LEN] = { 0 };
char group_name[NEU_GROUP_NAME_LEN] = { 0 };
int ret = 0;
char plugin_name[NEU_PLUGIN_FILTER_MAX * NEU_PLUGIN_NAME_LEN] = { 0 };
char node_name[NEU_NODE_NAME_LEN] = { 0 };
char group_name[NEU_GROUP_NAME_LEN] = { 0 };
neu_plugin_t * plugin = neu_rest_get_plugin();
neu_node_type_e node_type = { 0 };
@@ -226,9 +226,40 @@ void handle_get_adapter(nng_aio *aio)
return;
}
if (neu_http_get_param_str(aio, "plugin", plugin_name,
sizeof(plugin_name)) > 0) {
strncpy(cmd.plugin, plugin_name, NEU_PLUGIN_NAME_LEN - 1);
ssize_t plugin_len =
neu_http_get_param_str(aio, "plugin", plugin_name, sizeof(plugin_name));
if (plugin_len > 0) {
if (plugin_len >= (ssize_t) sizeof(plugin_name)) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, {
neu_http_response(aio, error_code.error, result_error);
})
return;
}
char *save = NULL;
char *p = strtok_r(plugin_name, ",", &save);
while (p != NULL) {
while (' ' == *p || '\t' == *p) {
p++;
}
size_t len = strlen(p);
while (len > 0 && (' ' == p[len - 1] || '\t' == p[len - 1])) {
p[--len] = '\0';
}
if (len == 0) {
p = strtok_r(NULL, ",", &save);
continue;
}
if (cmd.n_plugin >= NEU_PLUGIN_FILTER_MAX) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, {
neu_http_response(aio, error_code.error, result_error);
})
return;
}
strncpy(cmd.plugin[cmd.n_plugin], p, NEU_PLUGIN_NAME_LEN - 1);
cmd.n_plugin++;
p = strtok_r(NULL, ",", &save);
}
}
if (neu_http_get_param_str(aio, "node", node_name, sizeof(node_name)) > 0) {
+11 -5
View File
@@ -495,8 +495,11 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
neu_req_del_plugin_t *cmd = (neu_req_del_plugin_t *) &header[1];
neu_resp_error_t e = { 0 };
char plugins[1][NEU_PLUGIN_NAME_LEN] = { 0 };
strncpy(plugins[0], cmd->plugin, NEU_PLUGIN_NAME_LEN - 1);
UT_array *nodes = neu_manager_get_nodes(
manager, NEU_NA_TYPE_DRIVER | NEU_NA_TYPE_APP, cmd->plugin, "",
manager, NEU_NA_TYPE_DRIVER | NEU_NA_TYPE_APP, plugins, 1, "",
false, false, 0, false, 0, "");
if (nodes != NULL) {
@@ -690,8 +693,11 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
break;
}
char plugins[1][NEU_PLUGIN_NAME_LEN] = { 0 };
strncpy(plugins[0], module_name, NEU_PLUGIN_NAME_LEN - 1);
UT_array *nodes = neu_manager_get_nodes(
manager, NEU_NA_TYPE_DRIVER | NEU_NA_TYPE_APP, module_name, "",
manager, NEU_NA_TYPE_DRIVER | NEU_NA_TYPE_APP, plugins, 1, "",
false, false, 0, false, 0, "");
if (nodes != NULL) {
@@ -957,9 +963,9 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
case NEU_REQ_GET_NODE: {
neu_req_get_node_t *cmd = (neu_req_get_node_t *) &header[1];
UT_array * nodes = neu_manager_get_nodes(
manager, cmd->type, cmd->plugin, cmd->node, cmd->query.s_delay,
cmd->query.q_state, cmd->query.state, cmd->query.q_link,
cmd->query.link, cmd->query.q_group_name);
manager, cmd->type, cmd->plugin, cmd->n_plugin, cmd->node,
cmd->query.s_delay, cmd->query.q_state, cmd->query.state,
cmd->query.q_link, cmd->query.link, cmd->query.q_group_name);
neu_resp_get_node_t resp = { .nodes = nodes };
header->type = NEU_RESP_GET_NODE;
+5 -5
View File
@@ -113,15 +113,15 @@ int neu_manager_del_node(neu_manager_t *manager, const char *node_name)
neu_node_manager_del(manager->node_manager, node_name);
return NEU_ERR_SUCCESS;
}
UT_array *neu_manager_get_nodes(neu_manager_t *manager, int type,
const char *plugin, const char *node,
const char (*plugins)[NEU_PLUGIN_NAME_LEN],
int n_plugins, const char *node,
bool sort_delay, bool q_state, int state,
bool q_link, int link, const char *q_group_name)
{
return neu_node_manager_filter(manager->node_manager, type, plugin, node,
sort_delay, q_state, state, q_link, link,
q_group_name);
return neu_node_manager_filter(manager->node_manager, type, plugins,
n_plugins, node, sort_delay, q_state, state,
q_link, link, q_group_name);
}
int neu_manager_update_node_name(neu_manager_t *manager, const char *node,
+2 -1
View File
@@ -59,7 +59,8 @@ int neu_manager_add_node(neu_manager_t *manager, const char *node_name,
bool load);
int neu_manager_del_node(neu_manager_t *manager, const char *node_name);
UT_array *neu_manager_get_nodes(neu_manager_t *manager, int type,
const char *plugin, const char *node,
const char (*plugins)[NEU_PLUGIN_NAME_LEN],
int n_plugins, const char *node,
bool sort_delay, bool q_state, int state,
bool q_link, int link,
const char *q_group_name);
+14 -4
View File
@@ -272,7 +272,8 @@ static bool find_tag_in_tags(const char *tags, const char *tag)
}
UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
const char *plugin, const char *node,
const char (*plugins)[NEU_PLUGIN_NAME_LEN],
int n_plugins, const char *node,
bool sort_delay, bool q_state, int state,
bool q_link, int link,
const char *q_group_name)
@@ -287,9 +288,18 @@ UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
{
if (!el->is_static && el->display) {
if (el->adapter->module->type & type) {
if (strlen(plugin) > 0 &&
strcmp(el->adapter->module->module_name, plugin) != 0) {
continue;
if (n_plugins > 0) {
bool matched = false;
for (int i = 0; i < n_plugins; i++) {
if (strcmp(el->adapter->module->module_name,
plugins[i]) == 0) {
matched = true;
break;
}
}
if (!matched) {
continue;
}
}
if (strlen(node) > 0) {
char *name_find = strstr(el->adapter->name, node);
+2 -1
View File
@@ -52,7 +52,8 @@ uint16_t neu_node_manager_size(neu_node_manager_t *mgr);
// neu_resp_node_info array
UT_array *neu_node_manager_get(neu_node_manager_t *mgr, int type);
UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
const char *plugin, const char *node,
const char (*plugins)[NEU_PLUGIN_NAME_LEN],
int n_plugins, const char *node,
bool sort_delay, bool q_state, int state,
bool q_link, int link,
const char *q_group_name);
+7 -5
View File
@@ -94,11 +94,13 @@ def del_node(node, jwt=config.default_jwt):
return requests.delete(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, json={"name": node})
def get_nodes(type, jwt=config.default_jwt, group=""):
if group == "":
return requests.get(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, params={"type": type})
else:
return requests.get(url=config.BASE_URL + '/api/v2/node?group=' + group, headers={"Authorization": jwt}, params={"type": type})
def get_nodes(type, jwt=config.default_jwt, group="", plugins=None):
params = {"type": type}
if plugins:
params["plugin"] = ",".join(plugins)
if group != "":
params["group"] = group
return requests.get(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, params=params)
def get_nodes_by_tags(type, tags, jwt=config.default_jwt):
+27
View File
@@ -59,6 +59,33 @@ class TestNode:
assert 200 == response.status_code
assert "mqtt" == response.json()['nodes'][0]["name"]
@description(given="a driver node", when="get driver node by single plugin", then="get success")
def test_get_driver_by_single_plugin(self):
response = api.get_nodes(type=NEU_NODE_DRIVER, plugins=[PLUGIN_MODBUS_TCP])
assert 200 == response.status_code
assert 1 == len(response.json()['nodes'])
assert "modbus-tcp" == response.json()['nodes'][0]["name"]
@description(given="driver and app nodes", when="get driver nodes by multiple plugins", then="get success")
def test_get_driver_by_multiple_plugins(self):
response = api.get_nodes(type=NEU_NODE_DRIVER, plugins=[PLUGIN_MODBUS_TCP, PLUGIN_MODBUS_RTU])
assert 200 == response.status_code
assert 1 == len(response.json()['nodes'])
assert "modbus-tcp" == response.json()['nodes'][0]["name"]
@description(given="driver node and non-existent plugin", when="get driver node by plugins with unmatched", then="get success")
def test_get_driver_by_plugin_with_unmatched(self):
response = api.get_nodes(type=NEU_NODE_DRIVER, plugins=[PLUGIN_MODBUS_TCP, "non-existent-plugin"])
assert 200 == response.status_code
assert 1 == len(response.json()['nodes'])
assert "modbus-tcp" == response.json()['nodes'][0]["name"]
@description(given="driver node", when="get driver node by plugins no match", then="get empty")
def test_get_driver_by_plugin_no_match(self):
response = api.get_nodes(type=NEU_NODE_DRIVER, plugins=[PLUGIN_MODBUS_RTU])
assert 200 == response.status_code
assert [] == response.json()['nodes']
@description(given="existent driver node", when="update the node name to empty string", then="update failed")
def test_update_node_name_to_empty(self):
response = api.update_node(node="modbus-tcp", new_name="")