From 53c0a21d2cd019daee6d920c8966ad8136ea40f7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 5 Feb 2019 11:14:16 +1030 Subject: [PATCH] plugins: get usage from plugins (required unless deprecated_apis == True). Signed-off-by: Rusty Russell --- doc/PLUGINS.md | 9 +++++++-- lightningd/plugin.c | 24 ++++++++++++++++++++---- tests/test_plugin.py | 7 +++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index d4cb5f6c9..447de1296 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -70,10 +70,12 @@ this example: "rpcmethods": [ { "name": "hello", + "usage": "[name]", "description": "Returns a personalized greeting for {greeting} (set via options)." }, { "name": "gettime", + "usage": "", "description": "Returns the current time in {timezone}", "long_description": "Returns the current time in the timezone that is given as the only parameter.\nThis description may be quite long and is allowed to span multiple lines." } @@ -93,9 +95,10 @@ currently only string options are supported.* The `rpcmethods` are methods that will be exposed via `lightningd`'s JSON-RPC over Unix-Socket interface, just like the builtin commands. Any parameters given to the JSON-RPC calls will be passed -through verbatim. Notice that the `name` and the `description` fields +through verbatim. Notice that the `name`, `description` and `usage` fields are mandatory, while the `long_description` can be omitted (it'll be -set to `description` if it was not provided). +set to `description` if it was not provided). `usage` should surround optional +parameter names in `[]`. Plugins are free to register any `name` for their `rpcmethod` as long as the name was not previously registered. This includes both built-in @@ -149,11 +152,13 @@ called `hello` and `gettime`: "rpcmethods": [ { "name": "hello", + "usage": "[name]", "description": "Returns a personalized greeting for {greeting} (set via options)." }, { "name": "gettime", "description": "Returns the current time in {timezone}", + "usage": "", "long_description": "Returns the current time in the timezone that is given as the only parameter.\nThis description may be quite long and is allowed to span multiple lines." } ], diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 30343abbc..cd352e9e7 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -633,12 +634,14 @@ static bool plugin_rpcmethod_add(struct plugin *plugin, const char *buffer, const jsmntok_t *meth) { - const jsmntok_t *nametok, *desctok, *longdesctok; + const jsmntok_t *nametok, *desctok, *longdesctok, *usagetok; struct json_command *cmd; + const char *usage; nametok = json_get_member(buffer, meth, "name"); desctok = json_get_member(buffer, meth, "description"); longdesctok = json_get_member(buffer, meth, "long_description"); + usagetok = json_get_member(buffer, meth, "usage"); if (!nametok || nametok->type != JSMN_STRING) { plugin_kill(plugin, @@ -662,6 +665,13 @@ static bool plugin_rpcmethod_add(struct plugin *plugin, return false; } + if (usagetok && usagetok->type != JSMN_STRING) { + plugin_kill(plugin, + "\"usage\" is not a string: %.*s", + meth->end - meth->start, buffer + meth->start); + return false; + } + cmd = notleak(tal(plugin, struct json_command)); cmd->name = json_strdup(cmd, buffer, nametok); cmd->description = json_strdup(cmd, buffer, desctok); @@ -669,12 +679,18 @@ static bool plugin_rpcmethod_add(struct plugin *plugin, cmd->verbose = json_strdup(cmd, buffer, longdesctok); else cmd->verbose = cmd->description; + if (usagetok) + usage = json_strdup(tmpctx, buffer, usagetok); + else if (!deprecated_apis) { + plugin_kill(plugin, + "\"usage\" not provided by plugin"); + return false; + } else + usage = "[params]"; cmd->deprecated = false; cmd->dispatch = plugin_rpcmethod_dispatch; - if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd, - /* FIXME */ - "[params]")) { + if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd, usage)) { log_broken(plugin->log, "Could not register method \"%s\", a method with " "that name is already registered", diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 3e19fcfb4..f1e1ad757 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1,6 +1,7 @@ from collections import OrderedDict from fixtures import * # noqa: F401,F403 from lightning import RpcError +from utils import only_one import pytest import subprocess @@ -47,6 +48,8 @@ def test_rpc_passthrough(node_factory): cmd = [hlp for hlp in n.rpc.help()['help'] if 'hello' in hlp['command']] assert(len(cmd) == 1) + # Make sure usage message is present. + assert only_one(n.rpc.help('hello')['help'])['command'] == 'hello [name]' # While we're at it, let's check that helloworld.py is logging # correctly via the notifications plugin->lightningd assert n.daemon.is_in_log('Plugin helloworld.py initialized') @@ -119,3 +122,7 @@ def test_pay_plugin(node_factory): with pytest.raises(RpcError, match=r'missing required parameter'): l1.rpc.call('pay') + + # Make sure usage messages are present. + assert only_one(l1.rpc.help('pay')['help'])['command'] == 'pay bolt11 [msatoshi] [description] [riskfactor] [maxfeepercent] [retry_for] [maxdelay] [exemptfee]' + assert only_one(l1.rpc.help('paystatus')['help'])['command'] == 'paystatus [bolt11]'