From 551c12e049029ecc6f49cea0c94f3e4c3b0d2875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 8 Jun 2026 23:04:40 +0200 Subject: [PATCH] relay/api: add resource `GET /api/scripts` --- CHANGELOG.md | 1 + doc/en/weechat_relay_api.en.adoc | 46 +++++++++++++ doc/fr/weechat_relay_api.fr.adoc | 46 +++++++++++++ doc/sr/weechat_relay_api.sr.adoc | 48 ++++++++++++++ src/plugins/relay/api/relay-api-msg.c | 37 +++++++++++ src/plugins/relay/api/relay-api-msg.h | 2 + src/plugins/relay/api/relay-api-protocol.c | 59 +++++++++++++++++ src/plugins/relay/api/relay-api.h | 2 +- src/plugins/relay/api/weechat-relay-api.yaml | 57 ++++++++++++++++- .../plugins/relay/api/test-relay-api-msg.cpp | 64 +++++++++++++++++++ .../relay/api/test-relay-api-protocol.cpp | 61 ++++++++++++++++++ 11 files changed, 419 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd4dac5ee..9588bc6a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ SPDX-License-Identifier: GPL-3.0-or-later ### Added +- relay/api: add resource `GET /api/scripts` - relay: add option relay.network.unix_socket_permissions ([#2317](https://github.com/weechat/weechat/issues/2317)) - script: add info "script_languages" diff --git a/doc/en/weechat_relay_api.en.adoc b/doc/en/weechat_relay_api.en.adoc index 492201bf1..d606266c5 100644 --- a/doc/en/weechat_relay_api.en.adoc +++ b/doc/en/weechat_relay_api.en.adoc @@ -1179,6 +1179,51 @@ HTTP/1.1 200 OK ] ---- +[[resource_scripts]] +=== Scripts + +Return loaded scripts (all languages). + +Endpoint: + +---- +GET /api/scripts +---- + +Request example: + +[source,shell] +---- +curl -L -u 'plain:secret_password' 'https://localhost:9000/api/scripts' +---- + +Response: + +[source,http] +---- +HTTP/1.1 200 OK +---- + +[source,json] +---- +[ + { + "name": "highmon.pl", + "version": "2.7", + "description": "Highlight Monitor", + "author": "KenjiE20", + "license": "GPL3" + }, + { + "name": "go.py", + "version": "3.1.1", + "description": "Quick jump to buffers", + "author": "Sébastien Helleu ", + "license": "GPL3" + } +] +---- + [[resource_input]] === Input @@ -1510,6 +1555,7 @@ Body types that can be returned: * `nick_group` (object) * `nick` (object) * `hotlist` (object) +* `scripts` (array) * `ping` (object) [TIP] diff --git a/doc/fr/weechat_relay_api.fr.adoc b/doc/fr/weechat_relay_api.fr.adoc index 366440772..793815d9f 100644 --- a/doc/fr/weechat_relay_api.fr.adoc +++ b/doc/fr/weechat_relay_api.fr.adoc @@ -1191,6 +1191,51 @@ HTTP/1.1 200 OK ] ---- +[[resource_scripts]] +=== Scripts + +Retourner la liste des scripts chargés (tous les langages). + +Point de terminaison : + +---- +GET /api/scripts +---- + +Exemple de requête : + +[source,shell] +---- +curl -L -u 'plain:secret_password' 'https://localhost:9000/api/scripts' +---- + +Réponse : + +[source,http] +---- +HTTP/1.1 200 OK +---- + +[source,json] +---- +[ + { + "name": "highmon.pl", + "version": "2.7", + "description": "Highlight Monitor", + "author": "KenjiE20", + "license": "GPL3" + }, + { + "name": "go.py", + "version": "3.1.1", + "description": "Quick jump to buffers", + "author": "Sébastien Helleu ", + "license": "GPL3" + } +] +---- + [[resource_input]] === Entrée @@ -1530,6 +1575,7 @@ Les types de corps qui peuvent être retournés : * `nick_group` (objet) * `nick` (objet) * `hotlist` (objet) +* `scripts` (tableau) * `ping` (objet) [TIP] diff --git a/doc/sr/weechat_relay_api.sr.adoc b/doc/sr/weechat_relay_api.sr.adoc index 385d92a3a..8552a93f2 100644 --- a/doc/sr/weechat_relay_api.sr.adoc +++ b/doc/sr/weechat_relay_api.sr.adoc @@ -1181,6 +1181,53 @@ HTTP/1.1 200 OK ] ---- +// TRANSLATION MISSING +[[resource_scripts]] +=== Scripts + +// TRANSLATION MISSING +Return loaded scripts (all languages). + +Крајња тачка: + +---- +GET /api/scripts +---- + +Пример захтева: + +[source,shell] +---- +curl -L -u 'plain:secret_password' 'https://localhost:9000/api/scripts' +---- + +Одговор: + +[source,http] +---- +HTTP/1.1 200 OK +---- + +[source,json] +---- +[ + { + "name": "highmon.pl", + "version": "2.7", + "description": "Highlight Monitor", + "author": "KenjiE20", + "license": "GPL3" + }, + { + "name": "go.py", + "version": "3.1.1", + "description": "Quick jump to buffers", + "author": "Sébastien Helleu ", + "license": "GPL3" + } +] +---- + [[resource_input]] === Input @@ -1510,6 +1557,7 @@ GUID `258EAFA5-E914-47DA-95CA-C5AB0DC85B11` (SHA-1 се кодира у base64). * `nick_group` (објекат) * `nick` (објекат) * `hotlist` (објекат) +* `scripts` (низ) * `ping` (објекат) [TIP] diff --git a/src/plugins/relay/api/relay-api-msg.c b/src/plugins/relay/api/relay-api-msg.c index e018ba0e0..0c4abfaf9 100644 --- a/src/plugins/relay/api/relay-api-msg.c +++ b/src/plugins/relay/api/relay-api-msg.c @@ -848,3 +848,40 @@ relay_api_msg_hotlist_to_json (struct t_gui_hotlist *hotlist) return json; } + +/* + * Create a JSON object with a script. + */ + +cJSON * +relay_api_msg_script_to_json (struct t_hdata *hdata, void *script, const char *extension) +{ + cJSON *json; + void *pointer; + const char *ptr_string; + char name[1024]; + + if (!hdata) + return NULL; + + pointer = script; + + json = cJSON_CreateObject (); + if (!json) + return NULL; + + if (!script) + return json; + + snprintf (name, sizeof (name), + "%s.%s", + weechat_hdata_string (hdata, script, "name"), + extension); + MSG_ADD_STR_BUF("name", name); + MSG_ADD_HDATA_STR("version", "version"); + MSG_ADD_HDATA_STR("description", "description"); + MSG_ADD_HDATA_STR("author", "author"); + MSG_ADD_HDATA_STR("license", "license"); + + return json; +} diff --git a/src/plugins/relay/api/relay-api-msg.h b/src/plugins/relay/api/relay-api-msg.h index 4e8a960c6..e4a1f3c60 100644 --- a/src/plugins/relay/api/relay-api-msg.h +++ b/src/plugins/relay/api/relay-api-msg.h @@ -58,5 +58,7 @@ extern cJSON *relay_api_msg_nick_group_to_json (struct t_gui_nick_group *nick_gr enum t_relay_api_colors colors); extern cJSON *relay_api_msg_completion_to_json (struct t_gui_completion *completion); extern cJSON *relay_api_msg_hotlist_to_json (struct t_gui_hotlist *hotlist); +extern cJSON *relay_api_msg_script_to_json (struct t_hdata *hdata, void *script, + const char *extension); #endif /* WEECHAT_PLUGIN_RELAY_API_MSG_H */ diff --git a/src/plugins/relay/api/relay-api-protocol.c b/src/plugins/relay/api/relay-api-protocol.c index f17392165..120c3155b 100644 --- a/src/plugins/relay/api/relay-api-protocol.c +++ b/src/plugins/relay/api/relay-api-protocol.c @@ -764,6 +764,64 @@ RELAY_API_PROTOCOL_CALLBACK(hotlist) return RELAY_API_PROTOCOL_RC_OK; } +/* + * Callback for resource "scripts". + * + * Routes: + * GET /api/scripts + */ + +RELAY_API_PROTOCOL_CALLBACK(scripts) +{ + cJSON *json; + char *info_languages, **languages, hdata_name[256], *pos; + int num_languages, i; + struct t_hdata *ptr_hdata; + void *ptr_script; + + json = cJSON_CreateArray (); + if (!json) + return RELAY_API_PROTOCOL_RC_MEMORY; + + info_languages = weechat_info_get ("script_languages", NULL); + if (info_languages) + { + languages = weechat_string_split (info_languages, ",", NULL, + WEECHAT_STRING_SPLIT_STRIP_LEFT + | WEECHAT_STRING_SPLIT_STRIP_RIGHT + | WEECHAT_STRING_SPLIT_COLLAPSE_SEPS, + 0, &num_languages); + if (languages) + { + for (i = 0; i < num_languages; i++) + { + pos = strchr (languages[i], ':'); + if (pos) + { + pos[0] = '\0'; + snprintf (hdata_name, sizeof (hdata_name), + "%s_script", languages[i]); + ptr_hdata = weechat_hdata_get (hdata_name); + ptr_script = weechat_hdata_get_list (ptr_hdata, "scripts"); + while (ptr_script) + { + cJSON_AddItemToArray ( + json, + relay_api_msg_script_to_json (ptr_hdata, ptr_script, pos + 1)); + ptr_script = weechat_hdata_move (ptr_hdata, ptr_script, 1); + } + } + } + weechat_string_free_split (languages); + } + free (info_languages); + } + + relay_api_msg_send_json (client, RELAY_HTTP_200_OK, NULL, "scripts", json); + cJSON_Delete (json); + return RELAY_API_PROTOCOL_RC_OK; +} + /* * Callback for resource "input". * @@ -1286,6 +1344,7 @@ relay_api_protocol_recv_http (struct t_relay_client *client) { "GET", "version", 1, 0, 0, RELAY_API_CB(version) }, { "GET", "buffers", 1, 0, 3, RELAY_API_CB(buffers) }, { "GET", "hotlist", 1, 0, 3, RELAY_API_CB(hotlist) }, + { "GET", "scripts", 1, 0, 0, RELAY_API_CB(scripts) }, { "POST", "input", 1, 0, 0, RELAY_API_CB(input) }, { "POST", "completion", 1, 0, 0, RELAY_API_CB(completion) }, { "POST", "ping", 1, 0, 0, RELAY_API_CB(ping) }, diff --git a/src/plugins/relay/api/relay-api.h b/src/plugins/relay/api/relay-api.h index 60fab404c..714c95ea1 100644 --- a/src/plugins/relay/api/relay-api.h +++ b/src/plugins/relay/api/relay-api.h @@ -26,7 +26,7 @@ struct t_relay_client; enum t_relay_status; #define RELAY_API_VERSION_MAJOR 0 -#define RELAY_API_VERSION_MINOR 5 +#define RELAY_API_VERSION_MINOR 6 #define RELAY_API_VERSION_PATCH 0 #define RELAY_API_VERSION_NUMBER \ ((RELAY_API_VERSION_MAJOR << 16) \ diff --git a/src/plugins/relay/api/weechat-relay-api.yaml b/src/plugins/relay/api/weechat-relay-api.yaml index f44ec5aea..dda011957 100644 --- a/src/plugins/relay/api/weechat-relay-api.yaml +++ b/src/plugins/relay/api/weechat-relay-api.yaml @@ -17,7 +17,7 @@ info: license: name: CC BY-NC-SA 4.0 url: https://creativecommons.org/licenses/by-nc-sa/4.0/ - version: 0.5.0 + version: 0.6.0 externalDocs: url: https://weechat.org/doc/ @@ -32,6 +32,7 @@ tags: - name: version - name: buffers - name: hotlist + - name: scripts - name: input - name: completion - name: ping @@ -96,7 +97,7 @@ paths: get: tags: - version - description: Get the WeeChat and API versions + description: Get the WeeChat and API versions. parameters: - $ref: '#/components/parameters/totp' operationId: getVersion @@ -531,7 +532,7 @@ paths: get: tags: - hotlist - description: Get hotlist + description: Get hotlist. operationId: getHotlist parameters: - $ref: '#/components/parameters/totp' @@ -564,6 +565,43 @@ paths: $ref: '#/components/schemas/Error' security: - password: [] + /scripts: + get: + tags: + - scripts + description: Get list of loaded scripts. + operationId: getScripts + parameters: + - $ref: '#/components/parameters/totp' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Script' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: Out of memory + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + security: + - password: [] /input: post: tags: @@ -1336,6 +1374,19 @@ components: - date - buffer_id - count + Script: + type: object + properties: + name: + type: string + description: script name + example: 'go.py' + version: + type: string + example: '3.1.1' + required: + - name + - version Completion: type: object properties: diff --git a/tests/unit/plugins/relay/api/test-relay-api-msg.cpp b/tests/unit/plugins/relay/api/test-relay-api-msg.cpp index 2d273b9f8..d29c3b582 100644 --- a/tests/unit/plugins/relay/api/test-relay-api-msg.cpp +++ b/tests/unit/plugins/relay/api/test-relay-api-msg.cpp @@ -23,13 +23,17 @@ #include "CppUTest/TestHarness.h" +#include "tests.h" + extern "C" { #include #include #include #include "src/core/core-hdata.h" +#include "src/core/core-hook.h" #include "src/core/core-util.h" +#include "src/core/weechat.h" #include "src/gui/gui-buffer.h" #include "src/gui/gui-chat.h" #include "src/gui/gui-color.h" @@ -653,3 +657,63 @@ TEST(RelayApiMsg, HotlistToJson) gui_hotlist_remove_buffer (gui_buffers, 1); } + +/* + * Test functions: + * relay_api_msg_script_to_json + */ + +TEST(RelayApiMsg, ScriptToJson) +{ + struct t_hdata *ptr_hdata; + void *ptr_script; + cJSON *json, *json_obj; + char path_testapigen[PATH_MAX], path_testapi[PATH_MAX]; + char *test_scripts_dir, str_command[(PATH_MAX * 2) + 128]; + const char *ptr_test_scripts_dir; + + POINTERS_EQUAL(NULL, relay_api_msg_script_to_json (NULL, NULL, NULL)); + + ptr_hdata = hook_hdata_get (NULL, "python_script"); + + json = relay_api_msg_script_to_json (ptr_hdata, NULL, NULL); + CHECK(json); + CHECK(cJSON_IsObject (json)); + cJSON_Delete (json); + + /* load a python script for this test */ + ptr_test_scripts_dir = getenv ("WEECHAT_TESTS_SCRIPTS_DIR"); + test_scripts_dir = strdup ( + (ptr_test_scripts_dir) ? + ptr_test_scripts_dir : "./scripts/python"); + snprintf (path_testapigen, sizeof (path_testapigen), + "%s%s%s", + test_scripts_dir, + DIR_SEPARATOR, + "testapigen.py"); + snprintf (path_testapi, sizeof (path_testapi), + "%s%s%s", + test_scripts_dir, + DIR_SEPARATOR, + "testapi.py"); + snprintf (str_command, sizeof (str_command), + "/script load %s", path_testapigen); + run_cmd (str_command); + + ptr_script = hdata_get_list (ptr_hdata, "scripts"); + CHECK(ptr_script); + json = relay_api_msg_script_to_json (ptr_hdata, ptr_script, "py"); + CHECK(json); + CHECK(cJSON_IsObject (json)); + WEE_CHECK_OBJ_STR("testapigen.py", json, "name"); + WEE_CHECK_OBJ_STR("0.1", json, "version"); + WEE_CHECK_OBJ_STR("Generate scripting API test scripts", json, "description"); + WEE_CHECK_OBJ_STR("Sébastien Helleu ", json, "author"); + WEE_CHECK_OBJ_STR("GPL3", json, "license"); + cJSON_Delete (json); + + /* unload script */ + snprintf (str_command, sizeof (str_command), + "/script unload -q weechat_testapi.py"); + run_cmd (str_command); +} diff --git a/tests/unit/plugins/relay/api/test-relay-api-protocol.cpp b/tests/unit/plugins/relay/api/test-relay-api-protocol.cpp index 66d2f1041..2527efe8a 100644 --- a/tests/unit/plugins/relay/api/test-relay-api-protocol.cpp +++ b/tests/unit/plugins/relay/api/test-relay-api-protocol.cpp @@ -23,6 +23,7 @@ #include "CppUTest/TestHarness.h" +#include "tests.h" #include "tests-record.h" extern "C" @@ -34,6 +35,7 @@ extern "C" #include "src/core/core-string.h" #include "src/core/core-util.h" #include "src/core/core-version.h" +#include "src/core/weechat.h" #include "src/gui/gui-buffer.h" #include "src/gui/gui-chat.h" #include "src/gui/gui-hotlist.h" @@ -611,6 +613,65 @@ TEST(RelayApiProtocolWithClient, CbHotlist) gui_hotlist_remove_buffer (gui_buffers, 1); } +/* + * Test functions: + * relay_api_protocol_cb_scripts + */ + +TEST(RelayApiProtocolWithClient, CbScripts) +{ + cJSON *json, *json_obj; + char path_testapigen[PATH_MAX], path_testapi[PATH_MAX]; + char *test_scripts_dir, str_command[(PATH_MAX * 2) + 128]; + const char *ptr_test_scripts_dir; + + /* get scripts (no scripts loaded) */ + test_client_recv_http ("GET /api/scripts", NULL, NULL); + WEE_CHECK_HTTP_CODE(200, "OK"); + CHECK(json_body_sent[0]); + CHECK(cJSON_IsArray (json_body_sent[0])); + LONGS_EQUAL(0, cJSON_GetArraySize (json_body_sent[0])); + + /* load a python script for this test */ + ptr_test_scripts_dir = getenv ("WEECHAT_TESTS_SCRIPTS_DIR"); + test_scripts_dir = strdup ( + (ptr_test_scripts_dir) ? + ptr_test_scripts_dir : "./scripts/python"); + snprintf (path_testapigen, sizeof (path_testapigen), + "%s%s%s", + test_scripts_dir, + DIR_SEPARATOR, + "testapigen.py"); + snprintf (path_testapi, sizeof (path_testapi), + "%s%s%s", + test_scripts_dir, + DIR_SEPARATOR, + "testapi.py"); + snprintf (str_command, sizeof (str_command), + "/script load %s", path_testapigen); + run_cmd (str_command); + + /* get scripts (one loaded) */ + test_client_recv_http ("GET /api/scripts", NULL, NULL); + WEE_CHECK_HTTP_CODE(200, "OK"); + CHECK(json_body_sent[0]); + CHECK(cJSON_IsArray (json_body_sent[0])); + LONGS_EQUAL(1, cJSON_GetArraySize (json_body_sent[0])); + json = cJSON_GetArrayItem (json_body_sent[0], 0); + CHECK(json); + CHECK(cJSON_IsObject (json)); + WEE_CHECK_OBJ_STR("testapigen.py", json, "name"); + WEE_CHECK_OBJ_STR("0.1", json, "version"); + WEE_CHECK_OBJ_STR("Generate scripting API test scripts", json, "description"); + WEE_CHECK_OBJ_STR("Sébastien Helleu ", json, "author"); + WEE_CHECK_OBJ_STR("GPL3", json, "license"); + + /* unload script */ + snprintf (str_command, sizeof (str_command), + "/script unload -q weechat_testapi.py"); + run_cmd (str_command); +} + /* * Test functions: * relay_api_protocol_cb_completion