From 4174f3eee41631db92a581b0838f4a3657d4e725 Mon Sep 17 00:00:00 2001 From: hxy7yx <1595670487@qq.com> Date: Tue, 17 Mar 2026 19:58:53 -0700 Subject: [PATCH] feat(tag):supports renaming tags --- include/neuron/msg.h | 17 +++++ include/neuron/persist/persist.h | 11 +++ persistence/sqlite.db | Bin 0 -> 233472 bytes plugins/restful/datatag_handle.c | 66 ++++++++++++++++++ plugins/restful/datatag_handle.h | 2 + plugins/restful/handle.c | 9 +++ plugins/restful/rest.c | 7 ++ src/adapter/adapter.c | 41 ++++++++++++ src/adapter/driver/cache.c | 22 +++++- src/adapter/driver/cache.h | 2 + src/adapter/driver/driver.c | 24 +++++++ src/adapter/driver/driver_internal.h | 3 + src/adapter/storage.c | 11 +++ src/adapter/storage.h | 2 + src/base/group.c | 50 ++++++++++++++ src/base/group.h | 2 + src/base/msg_internal.h | 5 +- src/core/manager.c | 15 +++++ src/parser/neu_json_tag.c | 76 +++++++++++++++++++++ src/parser/neu_json_tag.h | 11 +++ src/persist/persist.c | 7 ++ src/persist/persist_impl.h | 12 ++++ src/persist/sqlite.c | 18 +++++ src/persist/sqlite.h | 4 ++ tests/ft/neuron/api.py | 4 ++ tests/ft/tag/test_tag.py | 96 +++++++++++++++++++++++++++ 26 files changed, 514 insertions(+), 3 deletions(-) create mode 100644 persistence/sqlite.db diff --git a/include/neuron/msg.h b/include/neuron/msg.h index 9e677ace..6d68d930 100644 --- a/include/neuron/msg.h +++ b/include/neuron/msg.h @@ -114,6 +114,8 @@ typedef enum neu_reqresp_type { NEU_RESP_UPDATE_TAG, NEU_REQ_GET_TAG, NEU_RESP_GET_TAG, + NEU_REQ_RENAME_TAG, + NEU_RESP_RENAME_TAG, NEU_REQ_ADD_PLUGIN, NEU_REQ_DEL_PLUGIN, @@ -169,6 +171,7 @@ typedef enum neu_reqresp_type { NEU_REQ_ADD_TAG_EVENT, NEU_REQ_DEL_TAG_EVENT, NEU_REQ_UPDATE_TAG_EVENT, + NEU_REQ_RENAME_TAG_EVENT, NEU_REQ_IMPORT_TAGS_EVENT, NEU_REQ_ADD_GTAG_EVENT, @@ -279,6 +282,8 @@ static const char *neu_reqresp_type_string_t[] = { [NEU_RESP_UPDATE_TAG] = "NEU_RESP_UPDATE_TAG", [NEU_REQ_GET_TAG] = "NEU_REQ_GET_TAG", [NEU_RESP_GET_TAG] = "NEU_RESP_GET_TAG", + [NEU_REQ_RENAME_TAG] = "NEU_REQ_RENAME_TAG", + [NEU_RESP_RENAME_TAG] = "NEU_RESP_RENAME_TAG", [NEU_REQ_ADD_PLUGIN] = "NEU_REQ_ADD_PLUGIN", [NEU_REQ_DEL_PLUGIN] = "NEU_REQ_DEL_PLUGIN", @@ -333,6 +338,7 @@ static const char *neu_reqresp_type_string_t[] = { [NEU_REQ_ADD_TAG_EVENT] = "NEU_REQ_ADD_TAG_EVENT", [NEU_REQ_DEL_TAG_EVENT] = "NEU_REQ_DEL_TAG_EVENT", [NEU_REQ_UPDATE_TAG_EVENT] = "NEU_REQ_UPDATE_TAG_EVENT", + [NEU_REQ_RENAME_TAG_EVENT] = "NEU_REQ_RENAME_TAG_EVENT", [NEU_REQ_IMPORT_TAGS_EVENT] = "NEU_REQ_IMPORT_TAGS_EVENT", [NEU_REQ_ADD_GTAG_EVENT] = "NEU_REQ_ADD_GTAG_EVENT", [NEU_REQ_ADD_PLUGIN_EVENT] = "NEU_REQ_ADD_PLUGIN_EVENT", @@ -730,6 +736,17 @@ typedef struct neu_resp_get_tag { UT_array *tags; // array neu_datatag_t } neu_resp_get_tag_t; +typedef struct { + char driver[NEU_NODE_NAME_LEN]; + char group[NEU_GROUP_NAME_LEN]; + char old_name[NEU_TAG_NAME_LEN]; + char new_name[NEU_TAG_NAME_LEN]; +} neu_req_rename_tag_t; + +typedef struct { + int error; +} neu_resp_rename_tag_t; + typedef struct { char app[NEU_NODE_NAME_LEN]; char driver[NEU_NODE_NAME_LEN]; diff --git a/include/neuron/persist/persist.h b/include/neuron/persist/persist.h index 6f0fd964..c0211f89 100644 --- a/include/neuron/persist/persist.h +++ b/include/neuron/persist/persist.h @@ -333,6 +333,17 @@ int neu_persister_update_tag_value(const char * driver_name, int neu_persister_delete_tag(const char *driver_name, const char *group_name, const char *tag_name); +/** + * Rename node tag. + * @param driver_name name of the driver who owns the tags + * @param group_name name of the group + * @param old_name old name of the tag + * @param new_name new name of the tag + * @return 0 on success, non-zero otherwise + */ +int neu_persister_rename_tag(const char *driver_name, const char *group_name, + const char *old_name, const char *new_name); + /** * Persist subscriptions. * @param app_name name of the app node diff --git a/persistence/sqlite.db b/persistence/sqlite.db new file mode 100644 index 0000000000000000000000000000000000000000..de6838f0a26f5fd8c213a9232a7ea643a9ee7d5c GIT binary patch literal 233472 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 literal 0 HcmV?d00001 diff --git a/plugins/restful/datatag_handle.c b/plugins/restful/datatag_handle.c index 574ab283..a9810a39 100644 --- a/plugins/restful/datatag_handle.c +++ b/plugins/restful/datatag_handle.c @@ -398,6 +398,72 @@ void handle_update_tags_resp(nng_aio *aio, neu_resp_update_tag_t *resp) handle_add_tags_resp(aio, resp); } +void handle_rename_tag(nng_aio *aio) +{ + neu_plugin_t *plugin = neu_rest_get_plugin(); + + NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT( + aio, neu_json_rename_tag_req_t, neu_json_decode_rename_tag_req, { + int ret = 0; + neu_reqresp_head_t header = { 0 }; + neu_req_rename_tag_t cmd = { 0 }; + int err_type; + + header.ctx = aio; + header.type = NEU_REQ_RENAME_TAG; + header.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM; + + if (strlen(req->node) >= NEU_NODE_NAME_LEN) { + err_type = NEU_ERR_NODE_NAME_TOO_LONG; + goto error; + } + + if (strlen(req->group) >= NEU_GROUP_NAME_LEN) { + err_type = NEU_ERR_GROUP_NAME_TOO_LONG; + goto error; + } + + if (strlen(req->old_name) == 0 || strlen(req->new_name) == 0) { + err_type = NEU_ERR_PARAM_IS_WRONG; + goto error; + } + + if (strlen(req->old_name) >= NEU_TAG_NAME_LEN) { + err_type = NEU_ERR_TAG_NAME_TOO_LONG; + goto error; + } + + if (strlen(req->new_name) >= NEU_TAG_NAME_LEN) { + err_type = NEU_ERR_TAG_NAME_TOO_LONG; + goto error; + } + + strcpy(cmd.driver, req->node); + strcpy(cmd.group, req->group); + strcpy(cmd.old_name, req->old_name); + strcpy(cmd.new_name, req->new_name); + + ret = neu_plugin_op(plugin, header, &cmd); + if (ret != 0) { + NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, { + neu_http_response(aio, NEU_ERR_IS_BUSY, result_error); + }); + } + goto success; + + error: + NEU_JSON_RESPONSE_ERROR( + err_type, { neu_http_response(aio, err_type, result_error); }); + success:; + }) +} + +void handle_rename_tag_resp(nng_aio *aio, neu_resp_rename_tag_t *resp) +{ + NEU_JSON_RESPONSE_ERROR( + resp->error, { neu_http_response(aio, resp->error, result_error); }); +} + void handle_get_tags(nng_aio *aio) { neu_plugin_t * plugin = neu_rest_get_plugin(); diff --git a/plugins/restful/datatag_handle.h b/plugins/restful/datatag_handle.h index 4d754e8e..3142221c 100644 --- a/plugins/restful/datatag_handle.h +++ b/plugins/restful/datatag_handle.h @@ -33,6 +33,8 @@ void handle_import_tags_resp(nng_aio *aio, neu_resp_add_tag_t *resp); void handle_del_tags(nng_aio *aio); void handle_update_tags(nng_aio *aio); void handle_update_tags_resp(nng_aio *aio, neu_resp_update_tag_t *resp); +void handle_rename_tag(nng_aio *aio); +void handle_rename_tag_resp(nng_aio *aio, neu_resp_rename_tag_t *resp); void handle_get_tags(nng_aio *aio); void handle_get_tags_resp(nng_aio *aio, neu_resp_get_tag_t *tags); diff --git a/plugins/restful/handle.c b/plugins/restful/handle.c index cbb6ecb1..d021db11 100644 --- a/plugins/restful/handle.c +++ b/plugins/restful/handle.c @@ -212,6 +212,9 @@ static struct neu_http_handler cors_handler[] = { { .url = "/api/v2/simulator/export", }, + { + .url = "/api/v2/tag/rename", + }, }; static struct neu_http_handler rest_handlers[] = { @@ -294,6 +297,12 @@ static struct neu_http_handler rest_handlers[] = { .url = "/api/v2/tags", .value.handler = handle_del_tags, }, + { + .method = NEU_HTTP_METHOD_PUT, + .type = NEU_HTTP_HANDLER_FUNCTION, + .url = "/api/v2/tag/rename", + .value.handler = handle_rename_tag, + }, { .method = NEU_HTTP_METHOD_POST, .type = NEU_HTTP_HANDLER_FUNCTION, diff --git a/plugins/restful/rest.c b/plugins/restful/rest.c index 64520145..0d6dc633 100644 --- a/plugins/restful/rest.c +++ b/plugins/restful/rest.c @@ -280,6 +280,13 @@ static int dashb_plugin_request(neu_plugin_t * plugin, scope, "error", ((neu_resp_update_tag_t *) data)->error); } break; + case NEU_RESP_RENAME_TAG: + handle_rename_tag_resp(header->ctx, (neu_resp_rename_tag_t *) data); + if (neu_otel_control_is_started() && trace) { + neu_otel_scope_add_span_attr_int( + scope, "error", ((neu_resp_rename_tag_t *) data)->error); + } + break; case NEU_RESP_GET_TAG: handle_get_tags_resp(header->ctx, (neu_resp_get_tag_t *) data); if (neu_otel_control_is_started() && trace) { diff --git a/src/adapter/adapter.c b/src/adapter/adapter.c index 62c0136e..b7ac6807 100644 --- a/src/adapter/adapter.c +++ b/src/adapter/adapter.c @@ -548,6 +548,11 @@ static int adapter_command(neu_adapter_t *adapter, neu_reqresp_head_t header, strcpy(pheader->receiver, cmd->driver); break; } + case NEU_REQ_RENAME_TAG: { + neu_req_rename_tag_t *cmd = (neu_req_rename_tag_t *) data; + strcpy(pheader->receiver, cmd->driver); + break; + } case NEU_REQ_IMPORT_TAGS: { neu_req_import_tags_t *cmd = (neu_req_import_tags_t *) data; strcpy(pheader->receiver, cmd->node); @@ -887,6 +892,7 @@ static int adapter_loop(enum neu_event_io_type type, int fd, void *usr_data) case NEU_RESP_ADD_GTAG: case NEU_RESP_IMPORT_TAGS: case NEU_RESP_UPDATE_TAG: + case NEU_RESP_RENAME_TAG: case NEU_RESP_GET_TAG: case NEU_RESP_GET_NODE: case NEU_RESP_GET_PLUGIN: @@ -912,6 +918,7 @@ static int adapter_loop(enum neu_event_io_type type, int fd, void *usr_data) case NEU_REQ_ADD_TAG_EVENT: case NEU_REQ_DEL_TAG_EVENT: case NEU_REQ_UPDATE_TAG_EVENT: + case NEU_REQ_RENAME_TAG_EVENT: case NEU_REQ_ADD_GTAG_EVENT: case NEU_REQ_ADD_PLUGIN_EVENT: case NEU_REQ_DEL_PLUGIN_EVENT: @@ -1772,6 +1779,40 @@ static int adapter_loop(enum neu_event_io_type type, int fd, void *usr_data) reply(adapter, header, &resp); break; } + case NEU_REQ_RENAME_TAG: { + neu_req_rename_tag_t *cmd = (neu_req_rename_tag_t *) &header[1]; + neu_resp_rename_tag_t resp = { 0 }; + + if (adapter->module->type == NEU_NA_TYPE_DRIVER) { + resp.error = neu_adapter_driver_rename_tag( + (neu_adapter_driver_t *) adapter, cmd->group, cmd->old_name, + cmd->new_name); + if (resp.error == 0) { + rv = adapter_storage_rename_tag(cmd->driver, cmd->group, + cmd->old_name, cmd->new_name); + if (rv != 0) { + // rollback in-memory rename on persistence failure + nlog_error("persist rename failed, rolling back " + "tag:%s->%s node:%s grp:%s", + cmd->old_name, cmd->new_name, cmd->driver, + cmd->group); + neu_adapter_driver_rename_tag( + (neu_adapter_driver_t *) adapter, cmd->group, + cmd->new_name, cmd->old_name); + resp.error = NEU_ERR_EINTERNAL; + } else if (header->monitor) { + notify_monitor(adapter, NEU_REQ_RENAME_TAG_EVENT, cmd); + } + } + } else { + resp.error = NEU_ERR_GROUP_NOT_ALLOW; + } + + neu_msg_exchange(header); + header->type = NEU_RESP_RENAME_TAG; + reply(adapter, header, &resp); + break; + } case NEU_REQ_NODE_UNINIT: { neu_req_node_uninit_t *cmd = (neu_req_node_uninit_t *) &header[1]; char name[NEU_NODE_NAME_LEN] = { 0 }; diff --git a/src/adapter/driver/cache.c b/src/adapter/driver/cache.c index d76941bf..d39a0c2f 100644 --- a/src/adapter/driver/cache.c +++ b/src/adapter/driver/cache.c @@ -71,8 +71,8 @@ inline static tkey_t to_key(const char *group, const char *tag) { tkey_t key = { 0 }; - strcpy(key.group, group); - strcpy(key.tag, tag); + strncpy(key.group, group, sizeof(key.group) - 1); + strncpy(key.tag, tag, sizeof(key.tag) - 1); return key; } @@ -1266,3 +1266,21 @@ void neu_driver_cache_del(neu_driver_cache_t *cache, const char *group, pthread_mutex_unlock(&cache->mtx); } + +void neu_driver_cache_rename(neu_driver_cache_t *cache, const char *group, + const char *old_tag, const char *new_tag) +{ + struct elem *elem = NULL; + tkey_t key = to_key(group, old_tag); + + pthread_mutex_lock(&cache->mtx); + HASH_FIND(hh, cache->table, &key, sizeof(tkey_t), elem); + + if (elem != NULL) { + HASH_DEL(cache->table, elem); + elem->key = to_key(group, new_tag); + HASH_ADD(hh, cache->table, key, sizeof(tkey_t), elem); + } + + pthread_mutex_unlock(&cache->mtx); +} diff --git a/src/adapter/driver/cache.h b/src/adapter/driver/cache.h index 76c24442..dc7ff614 100644 --- a/src/adapter/driver/cache.h +++ b/src/adapter/driver/cache.h @@ -43,6 +43,8 @@ bool neu_driver_cache_update_change(neu_driver_cache_t *cache, void neu_driver_cache_del(neu_driver_cache_t *cache, const char *group, const char *tag); +void neu_driver_cache_rename(neu_driver_cache_t *cache, const char *group, + const char *old_tag, const char *new_tag); void neu_driver_cache_update_trace(neu_driver_cache_t *cache, const char *group, void *trace_ctx); diff --git a/src/adapter/driver/driver.c b/src/adapter/driver/driver.c index e29ae785..60a5e5eb 100644 --- a/src/adapter/driver/driver.c +++ b/src/adapter/driver/driver.c @@ -2320,6 +2320,30 @@ int neu_adapter_driver_update_tag(neu_adapter_driver_t *driver, return ret; } +int neu_adapter_driver_rename_tag(neu_adapter_driver_t *driver, + const char *group, const char *old_name, + const char *new_name) +{ + int ret = NEU_ERR_SUCCESS; + group_t *find = NULL; + + if (strlen(new_name) >= NEU_TAG_NAME_LEN) { + return NEU_ERR_TAG_NAME_TOO_LONG; + } + + HASH_FIND_STR(driver->groups, group, find); + if (find != NULL) { + ret = neu_group_rename_tag(find->group, old_name, new_name); + if (ret == NEU_ERR_SUCCESS) { + neu_driver_cache_rename(driver->cache, group, old_name, new_name); + } + } else { + ret = NEU_ERR_GROUP_NOT_EXIST; + } + + return ret; +} + int neu_adapter_driver_get_tag(neu_adapter_driver_t *driver, const char *group, UT_array **tags) { diff --git a/src/adapter/driver/driver_internal.h b/src/adapter/driver/driver_internal.h index 4a1c217e..21cb1abd 100644 --- a/src/adapter/driver/driver_internal.h +++ b/src/adapter/driver/driver_internal.h @@ -78,6 +78,9 @@ int neu_adapter_driver_del_tag(neu_adapter_driver_t *driver, const char *group, const char *tag); int neu_adapter_driver_update_tag(neu_adapter_driver_t *driver, const char *group, neu_datatag_t *tag); +int neu_adapter_driver_rename_tag(neu_adapter_driver_t *driver, + const char *group, const char *old_name, + const char *new_name); int neu_adapter_driver_get_tag(neu_adapter_driver_t *driver, const char *group, UT_array **tags); UT_array *neu_adapter_driver_get_ptag(neu_adapter_driver_t *driver, diff --git a/src/adapter/storage.c b/src/adapter/storage.c index f658a558..aa364ed8 100644 --- a/src/adapter/storage.c +++ b/src/adapter/storage.c @@ -127,6 +127,17 @@ void adapter_storage_del_tag(const char *node, const char *group, } } +int adapter_storage_rename_tag(const char *node, const char *group, + const char *old_name, const char *new_name) +{ + int rv = neu_persister_rename_tag(node, group, old_name, new_name); + if (0 != rv) { + nlog_error("fail rename tag:%s->%s adapter:%s grp:%s", old_name, + new_name, node, group); + } + return rv; +} + int adapter_load_setting(const char *node, char **setting) { int rv = neu_persister_load_node_setting(node, (const char **) setting); diff --git a/src/adapter/storage.h b/src/adapter/storage.h index 03a3ad5a..a933ef8a 100644 --- a/src/adapter/storage.h +++ b/src/adapter/storage.h @@ -41,6 +41,8 @@ void adapter_storage_update_tag_value(const char *node, const char *group, const neu_datatag_t *tag); void adapter_storage_del_tag(const char *node, const char *group, const char *name); +int adapter_storage_rename_tag(const char *node, const char *group, + const char *old_name, const char *new_name); int adapter_load_setting(const char *node, char **setting); int adapter_load_group_and_tag(neu_adapter_driver_t *driver); diff --git a/src/base/group.c b/src/base/group.c index 3dbdb03e..a7cf2154 100644 --- a/src/base/group.c +++ b/src/base/group.c @@ -157,6 +157,56 @@ int neu_group_update_tag(neu_group_t *group, const neu_datatag_t *tag) return ret; } +int neu_group_rename_tag(neu_group_t *group, const char *old_name, + const char *new_name) +{ + tag_elem_t *el = NULL; + tag_elem_t *conflict = NULL; + int ret = NEU_ERR_TAG_NOT_EXIST; + + pthread_mutex_lock(&group->mtx); + HASH_FIND_STR(group->tags, old_name, el); + if (el == NULL) { + pthread_mutex_unlock(&group->mtx); + return ret; + } + + // short-circuit: renaming to the same name is a no-op + if (strcmp(old_name, new_name) == 0) { + pthread_mutex_unlock(&group->mtx); + return NEU_ERR_SUCCESS; + } + + HASH_FIND_STR(group->tags, new_name, conflict); + if (conflict != NULL) { + pthread_mutex_unlock(&group->mtx); + return NEU_ERR_TAG_NAME_CONFLICT; + } + + // allocate new names before freeing old ones to ensure atomicity on OOM + char *new_el_name = strdup(new_name); + char *new_tag_name = strdup(new_name); + if (new_el_name == NULL || new_tag_name == NULL) { + free(new_el_name); + free(new_tag_name); + pthread_mutex_unlock(&group->mtx); + return NEU_ERR_EINTERNAL; + } + + HASH_DEL(group->tags, el); + free(el->name); + el->name = new_el_name; + free(el->tag->name); + el->tag->name = new_tag_name; + HASH_ADD_STR(group->tags, name, el); + + update_timestamp(group); + ret = NEU_ERR_SUCCESS; + pthread_mutex_unlock(&group->mtx); + + return ret; +} + int neu_group_del_tag(neu_group_t *group, const char *tag_name) { tag_elem_t *el = NULL; diff --git a/src/base/group.h b/src/base/group.h index 6b20772f..787970d7 100644 --- a/src/base/group.h +++ b/src/base/group.h @@ -37,6 +37,8 @@ void neu_group_destroy(neu_group_t *group); int neu_group_update(neu_group_t *group, uint32_t interval); int neu_group_add_tag(neu_group_t *group, const neu_datatag_t *tag); int neu_group_update_tag(neu_group_t *group, const neu_datatag_t *tag); +int neu_group_rename_tag(neu_group_t *group, const char *old_name, + const char *new_name); int neu_group_del_tag(neu_group_t *group, const char *tag_name); UT_array * neu_group_get_tag(neu_group_t *group); UT_array * neu_group_query_tag(neu_group_t *group, const char *name); diff --git a/src/base/msg_internal.h b/src/base/msg_internal.h index 33fb5989..392d7fb1 100644 --- a/src/base/msg_internal.h +++ b/src/base/msg_internal.h @@ -132,6 +132,7 @@ extern "C" { XX(NEU_REQ_ADD_TAG_EVENT, neu_req_add_tag_t) \ XX(NEU_REQ_DEL_TAG_EVENT, neu_req_del_tag_t) \ XX(NEU_REQ_UPDATE_TAG_EVENT, neu_req_update_tag_t) \ + XX(NEU_REQ_RENAME_TAG_EVENT, neu_req_rename_tag_t) \ XX(NEU_REQ_ADD_GTAG_EVENT, neu_req_add_gtag_t) \ XX(NEU_REQ_IMPORT_TAGS_EVENT, neu_req_import_tags_t) \ XX(NEU_REQ_ADD_PLUGIN_EVENT, neu_req_add_plugin_t) \ @@ -171,7 +172,9 @@ extern "C" { XX(NEU_REQ_SERVER_AUTH_USER_DELETE, neu_req_server_auth_user_del_t) \ XX(NEU_REQ_SERVER_AUTH_USER_UPDATE_PWD, neu_req_server_auth_user_update_t) \ XX(NEU_REQ_SERVER_AUTH_USER_INFO, neu_req_server_auth_users_info_t) \ - XX(NEU_RESP_SERVER_AUTH_USER_INFO, neu_resp_server_auth_users_info_t) + XX(NEU_RESP_SERVER_AUTH_USER_INFO, neu_resp_server_auth_users_info_t) \ + XX(NEU_REQ_RENAME_TAG, neu_req_rename_tag_t) \ + XX(NEU_RESP_RENAME_TAG, neu_resp_rename_tag_t) static inline size_t neu_reqresp_size(neu_reqresp_type_e t) { diff --git a/src/core/manager.c b/src/core/manager.c index 5a36f0fd..4a974bd7 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1515,6 +1515,19 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) break; } + case NEU_REQ_RENAME_TAG: { + if (neu_node_manager_find(manager->node_manager, header->receiver) == + NULL) { + neu_resp_error_t e = { .error = NEU_ERR_NODE_NOT_EXIST }; + header->type = NEU_RESP_ERROR; + neu_msg_exchange(header); + reply(manager, header, &e); + } else { + forward_msg(manager, header, header->receiver); + } + + break; + } case NEU_REQ_ADD_GTAG: { neu_req_add_gtag_t *cmd = (neu_req_add_gtag_t *) &header[1]; @@ -1593,6 +1606,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) case NEU_RESP_SERVER_SECURITY_POLICY_STATUS: case NEU_RESP_SERVER_AUTH_SWITCH_STATUS: case NEU_RESP_SERVER_AUTH_USER_INFO: + case NEU_RESP_RENAME_TAG: forward_msg(manager, header, header->receiver); break; @@ -1739,6 +1753,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) case NEU_REQ_ADD_TAG_EVENT: case NEU_REQ_DEL_TAG_EVENT: case NEU_REQ_UPDATE_TAG_EVENT: + case NEU_REQ_RENAME_TAG_EVENT: case NEU_REQ_ADD_GTAG_EVENT: case NEU_REQ_ADD_PLUGIN_EVENT: case NEU_REQ_DEL_PLUGIN_EVENT: diff --git a/src/parser/neu_json_tag.c b/src/parser/neu_json_tag.c index b1125f5e..05280bbe 100644 --- a/src/parser/neu_json_tag.c +++ b/src/parser/neu_json_tag.c @@ -1018,3 +1018,79 @@ int neu_json_encode_import_tags_resp(void *json_object, void *param) return ret; } + +int neu_json_decode_rename_tag_req(char * buf, + neu_json_rename_tag_req_t **result) +{ + int ret = 0; + void * json_obj = NULL; + neu_json_rename_tag_req_t *req = + calloc(1, sizeof(neu_json_rename_tag_req_t)); + if (req == NULL) { + return -1; + } + + json_obj = neu_json_decode_new(buf); + if (NULL == json_obj) { + free(req); + return -1; + } + + neu_json_elem_t req_elems[] = { + { + .name = "node", + .t = NEU_JSON_STR, + }, + { + .name = "group", + .t = NEU_JSON_STR, + }, + { + .name = "old_name", + .t = NEU_JSON_STR, + }, + { + .name = "new_name", + .t = NEU_JSON_STR, + }, + }; + ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems), + req_elems); + if (ret != 0) { + goto decode_fail; + } + + req->node = req_elems[0].v.val_str; + req->group = req_elems[1].v.val_str; + req->old_name = req_elems[2].v.val_str; + req->new_name = req_elems[3].v.val_str; + *result = req; + goto decode_exit; + +decode_fail: + free(req); + free(req_elems[0].v.val_str); + free(req_elems[1].v.val_str); + free(req_elems[2].v.val_str); + free(req_elems[3].v.val_str); + ret = -1; + +decode_exit: + if (json_obj != NULL) { + neu_json_decode_free(json_obj); + } + return ret; +} + +void neu_json_decode_rename_tag_req_free(neu_json_rename_tag_req_t *req) +{ + if (NULL == req) { + return; + } + + free(req->node); + free(req->group); + free(req->old_name); + free(req->new_name); + free(req); +} diff --git a/src/parser/neu_json_tag.h b/src/parser/neu_json_tag.h index bc98979e..fd4762df 100644 --- a/src/parser/neu_json_tag.h +++ b/src/parser/neu_json_tag.h @@ -159,6 +159,17 @@ typedef struct { int neu_json_encode_import_tags_resp(void *json_object, void *param); +typedef struct { + char *node; + char *group; + char *old_name; + char *new_name; +} neu_json_rename_tag_req_t; + +int neu_json_decode_rename_tag_req(char * buf, + neu_json_rename_tag_req_t **result); +void neu_json_decode_rename_tag_req_free(neu_json_rename_tag_req_t *req); + #ifdef __cplusplus } #endif diff --git a/src/persist/persist.c b/src/persist/persist.c index a24b730f..385e77ea 100644 --- a/src/persist/persist.c +++ b/src/persist/persist.c @@ -393,6 +393,13 @@ int neu_persister_delete_tag(const char *driver_name, const char *group_name, return g_impl->vtbl->delete_tag(g_impl, driver_name, group_name, tag_name); } +int neu_persister_rename_tag(const char *driver_name, const char *group_name, + const char *old_name, const char *new_name) +{ + return g_impl->vtbl->rename_tag(g_impl, driver_name, group_name, old_name, + new_name); +} + int neu_persister_store_subscription(const char *app_name, const char *driver_name, const char *group_name, const char *params, diff --git a/src/persist/persist_impl.h b/src/persist/persist_impl.h index bf74f1b1..63d6eb38 100644 --- a/src/persist/persist_impl.h +++ b/src/persist/persist_impl.h @@ -174,6 +174,18 @@ struct neu_persister_vtbl_s { int (*delete_tag)(neu_persister_t *self, const char *driver_name, const char *group_name, const char *tag_name); + /** + * Rename node tag. + * @param driver_name name of the driver who owns the tags + * @param group_name name of the group + * @param old_name old name of the tag + * @param new_name new name of the tag + * @return 0 on success, non-zero otherwise + */ + int (*rename_tag)(neu_persister_t *self, const char *driver_name, + const char *group_name, const char *old_name, + const char *new_name); + /** * Persist subscriptions. * @param app_name name of the app node diff --git a/src/persist/sqlite.c b/src/persist/sqlite.c index 20337e1b..5c7202af 100644 --- a/src/persist/sqlite.c +++ b/src/persist/sqlite.c @@ -422,6 +422,7 @@ static struct neu_persister_vtbl_s g_sqlite_persister_vtbl = { .update_tag = neu_sqlite_persister_update_tag, .update_tag_value = neu_sqlite_persister_update_tag_value, .delete_tag = neu_sqlite_persister_delete_tag, + .rename_tag = neu_sqlite_persister_rename_tag, .store_subscription = neu_sqlite_persister_store_subscription, .update_subscription = neu_sqlite_persister_update_subscription, .load_subscriptions = neu_sqlite_persister_load_subscriptions, @@ -890,6 +891,23 @@ int neu_sqlite_persister_delete_tag(neu_persister_t *self, return rv; } +int neu_sqlite_persister_rename_tag(neu_persister_t *self, + const char * driver_name, + const char * group_name, + const char *old_name, const char *new_name) +{ + sqlite3 *db = ((neu_sqlite_persister_t *) self)->db; + int rv = execute_sql(db, + "UPDATE tags SET name=%Q " + "WHERE driver_name=%Q AND group_name=%Q AND name=%Q", + new_name, driver_name, group_name, old_name); + if (0 == rv && 0 == sqlite3_changes(db)) { + nlog_warn("rename tag affected 0 rows: %s/%s %s->%s", driver_name, + group_name, old_name, new_name); + } + return rv; +} + int neu_sqlite_persister_store_subscription( neu_persister_t *self, const char *app_name, const char *driver_name, const char *group_name, const char *params, const char *static_tags) diff --git a/src/persist/sqlite.h b/src/persist/sqlite.h index aa4ecfca..c9d8ba35 100644 --- a/src/persist/sqlite.h +++ b/src/persist/sqlite.h @@ -74,6 +74,10 @@ int neu_sqlite_persister_delete_tag(neu_persister_t *self, const char * driver_name, const char * group_name, const char * tag_name); +int neu_sqlite_persister_rename_tag(neu_persister_t *self, + const char * driver_name, + const char * group_name, + const char *old_name, const char *new_name); int neu_sqlite_persister_store_subscription( neu_persister_t *self, const char *app_name, const char *driver_name, const char *group_name, const char *params, const char *static_tags); diff --git a/tests/ft/neuron/api.py b/tests/ft/neuron/api.py index 4f17ade8..43ea7966 100644 --- a/tests/ft/neuron/api.py +++ b/tests/ft/neuron/api.py @@ -177,6 +177,10 @@ def get_tags(node, group): return requests.get(url=config.BASE_URL + "/api/v2/tags", headers={"Authorization": config.default_jwt}, params={"node": node, "group": group}) +def rename_tag(node, group, old_name, new_name): + return requests.put(url=config.BASE_URL + '/api/v2/tag/rename', headers={"Authorization": config.default_jwt}, json={"node": node, "group": group, "old_name": old_name, "new_name": new_name}) + + def read_tags(node, group, sync=False, query=None): body = {"node": node, "group": group, "sync": sync} if query: diff --git a/tests/ft/tag/test_tag.py b/tests/ft/tag/test_tag.py index 400362f5..32b0acce 100644 --- a/tests/ft/tag/test_tag.py +++ b/tests/ft/tag/test_tag.py @@ -917,3 +917,99 @@ class TestTag: assert 400 == response.status_code assert NEU_ERR_TAG_DECIMAL_INVALID == response.json()['error'] assert 1 == response.json()['index'] + + @description(given="an existing tag", when="renaming to a new name", then="successfully renamed") + def test_rename_tag_success(self): + tag = [{"name": "rename_test_tag", "address": "1!400001", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}] + api.add_tags(node="modbus-tcp", group="group1", tags=tag) + + response = api.rename_tag(node="modbus-tcp", group="group1", + old_name="rename_test_tag", new_name="renamed_tag") + assert 200 == response.status_code + assert NEU_ERR_SUCCESS == response.json()['error'] + + response = api.get_tags(node="modbus-tcp", group="group1") + assert 200 == response.status_code + tag_names = [t['name'] for t in response.json()['tags']] + assert "renamed_tag" in tag_names + assert "rename_test_tag" not in tag_names + + api.del_tags(node="modbus-tcp", group="group1", tags=["renamed_tag"]) + + @description(given="an existing tag", when="renaming to a name that already exists", then="fail with conflict error") + def test_rename_tag_conflict(self): + tag1 = [{"name": "rename_tag_a", "address": "1!400001", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}] + tag2 = [{"name": "rename_tag_b", "address": "1!400002", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}] + api.add_tags(node="modbus-tcp", group="group1", tags=tag1) + api.add_tags(node="modbus-tcp", group="group1", tags=tag2) + + response = api.rename_tag(node="modbus-tcp", group="group1", + old_name="rename_tag_a", new_name="rename_tag_b") + assert 409 == response.status_code + assert NEU_ERR_TAG_NAME_CONFLICT == response.json()['error'] + + api.del_tags(node="modbus-tcp", group="group1", tags=["rename_tag_a", "rename_tag_b"]) + + @description(given="a non-existing tag", when="renaming", then="fail with not exist error") + def test_rename_tag_not_exist(self): + response = api.rename_tag(node="modbus-tcp", group="group1", + old_name="non_exist_tag", new_name="new_name") + assert 404 == response.status_code + assert NEU_ERR_TAG_NOT_EXIST == response.json()['error'] + + @description(given="an existing tag", when="renaming to the same name", then="success as no-op") + def test_rename_tag_same_name(self): + tag = [{"name": "rename_same_tag", "address": "1!400001", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}] + api.add_tags(node="modbus-tcp", group="group1", tags=tag) + + response = api.rename_tag(node="modbus-tcp", group="group1", + old_name="rename_same_tag", new_name="rename_same_tag") + assert 200 == response.status_code + assert NEU_ERR_SUCCESS == response.json()['error'] + + api.del_tags(node="modbus-tcp", group="group1", tags=["rename_same_tag"]) + + @description(given="an empty new_name", when="renaming", then="fail with name too long error") + def test_rename_tag_empty_new_name(self): + tag = [{"name": "rename_empty_tag", "address": "1!400001", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}] + api.add_tags(node="modbus-tcp", group="group1", tags=tag) + + response = api.rename_tag(node="modbus-tcp", group="group1", + old_name="rename_empty_tag", new_name="") + assert 400 == response.status_code + assert NEU_ERR_PARAM_IS_WRONG == response.json()['error'] + + api.del_tags(node="modbus-tcp", group="group1", tags=["rename_empty_tag"]) + + @description(given="a new_name exceeding max length", when="renaming", then="fail with name too long error") + def test_rename_tag_name_too_long(self): + tag = [{"name": "rename_long_tag", "address": "1!400001", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}] + api.add_tags(node="modbus-tcp", group="group1", tags=tag) + + long_name = "a" * 128 + response = api.rename_tag(node="modbus-tcp", group="group1", + old_name="rename_long_tag", new_name=long_name) + assert 400 == response.status_code + assert NEU_ERR_TAG_NAME_TOO_LONG == response.json()['error'] + + api.del_tags(node="modbus-tcp", group="group1", tags=["rename_long_tag"]) + + @description(given="a non-existing group", when="renaming tag", then="fail with group not exist") + def test_rename_tag_group_not_exist(self): + response = api.rename_tag(node="modbus-tcp", group="no_such_group", + old_name="some_tag", new_name="new_tag") + assert 404 == response.status_code + assert NEU_ERR_GROUP_NOT_EXIST == response.json()['error'] + + @description(given="a non-existing node", when="renaming tag", then="fail with node not exist") + def test_rename_tag_node_not_exist(self): + response = api.rename_tag(node="no_such_node", group="group1", + old_name="some_tag", new_name="new_tag") + assert 404 == response.status_code + assert NEU_ERR_NODE_NOT_EXIST == response.json()['error']