Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add clnrest object to plugin manifest #7507

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -1462,10 +1462,17 @@
},
"HelpHelp": {
"Help.help[].category": 2,
"Help.help[].clnrest": 5,
"Help.help[].command": 1,
"Help.help[].description": 3,
"Help.help[].verbose": 4
},
"HelpHelpClnrest": {
"Help.help[].clnrest.content_type": 3,
"Help.help[].clnrest.method": 2,
"Help.help[].clnrest.path": 1,
"Help.help[].clnrest.rune": 4
},
"HelpRequest": {
"Help.command": 1
},
Expand Down Expand Up @@ -6184,6 +6191,26 @@
"added": "pre-v0.10.1",
"deprecated": null
},
"Help.help[].clnrest": {
"added": "v24.08",
"deprecated": null
},
"Help.help[].clnrest.content_type": {
"added": "v24.08",
"deprecated": null
},
"Help.help[].clnrest.method": {
"added": "v24.08",
"deprecated": null
},
"Help.help[].clnrest.path": {
"added": "v24.08",
"deprecated": null
},
"Help.help[].clnrest.rune": {
"added": "v24.08",
"deprecated": null
},
"Help.help[].command": {
"added": "pre-v0.10.1",
"deprecated": null
Expand Down
8 changes: 8 additions & 0 deletions cln-grpc/proto/node.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions cln-grpc/src/convert.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions cln-rpc/src/model.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -12779,6 +12779,39 @@
"description": [
"The command."
]
},
"clnrest": {
"type": "object",
"additionalProperties": false,
"required": [
"path",
"method",
"content_type",
"rune"
],
"added": "v24.08",
"properties": {
"path": {
"type": "string",
"description": "the path to the HTTP endpoint for this command",
"added": "v24.08"
},
"method": {
"type": "string",
"description": "the HTTP method for this command",
"added": "v24.08"
},
"content_type": {
"type": "string",
"description": "http content-type that clnrest should return",
"added": "v24.08"
},
"rune": {
"type": "boolean",
"description": "whether or not this command requires a rune for authentication",
"added": "v24.08"
}
}
}
}
}
Expand Down
30 changes: 22 additions & 8 deletions contrib/pyln-client/pyln/client/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from dataclasses import dataclass
from enum import Enum
from threading import RLock
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, TypedDict

from .lightning import LightningRpc, Millisatoshi

Expand All @@ -37,6 +37,11 @@ class RequestState(Enum):
FINISHED = 'finished'
FAILED = 'failed'

class CLNRestData(TypedDict):
path: str
method: str
Comment on lines +41 to +42
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding path and method seems useless as these cannot be changed for clnrest. Path will always be the name of the rpc command (@rpcns.route("/<rpc_method>")) and method is always POST (def post(self, rpc_method)). We can add GET, DELETE etc. methods types in clnrest but it seems unnecessary because POST can be used for all types.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking clnrest could be modified to register path and method dynamically. Already been talking to @daywalker90 about adding that feature to his rust version of clnrest (#7509)
This is why I also opened #7508, so that clnrest could dynamically add routes as new plugins start, and remove those routes when plugins stop.
True that POST can be used for all types, but a plugin may want to implement a specific protocol that defines what the method should be for some endpoint. Note "Motivations" above...

content_type: str
rune: bool

class Method(object):
"""Description of methods that are registered with the plugin.
Expand All @@ -49,7 +54,8 @@ class Method(object):
def __init__(self, name: str, func: Callable[..., JSONType],
mtype: MethodType = MethodType.RPCMETHOD,
category: str = None, desc: str = None,
long_desc: str = None, deprecated: Union[bool, List[str]] = None):
long_desc: str = None, deprecated: Union[bool, List[str]] = None,
clnrest_data: CLNRestData = None):
self.name = name
self.func = func
self.mtype = mtype
Expand All @@ -60,6 +66,7 @@ def __init__(self, name: str, func: Callable[..., JSONType],
self.deprecated = deprecated
self.before: List[str] = []
self.after: List[str] = []
self.clnrest = clnrest_data


class RpcException(Exception):
Expand Down Expand Up @@ -330,7 +337,8 @@ def add_method(self, name: str, func: Callable[..., Any],
category: Optional[str] = None,
desc: Optional[str] = None,
long_desc: Optional[str] = None,
deprecated: Optional[Union[bool, List[str]]] = None) -> None:
deprecated: Optional[Union[bool, List[str]]] = None,
clnrest_data: CLNRestData = None) -> None:
"""Add a plugin method to the dispatch table.

The function will be expected at call time (see `_dispatch`)
Expand Down Expand Up @@ -372,7 +380,7 @@ def add_method(self, name: str, func: Callable[..., Any],
# Register the function with the name
method = Method(
name, func, MethodType.RPCMETHOD, category, desc, long_desc,
deprecated
deprecated, clnrest_data
)

method.background = background
Expand Down Expand Up @@ -491,22 +499,24 @@ def get_option(self, name: str) -> Optional[Any]:
def async_method(self, method_name: str, category: Optional[str] = None,
desc: Optional[str] = None,
long_desc: Optional[str] = None,
deprecated: Optional[Union[bool, List[str]]] = None) -> NoneDecoratorType:
deprecated: Optional[Union[bool, List[str]]] = None,
clnrest_data: CLNRestData = None) -> NoneDecoratorType:
"""Decorator to add an async plugin method to the dispatch table.

Internally uses add_method.
"""
def decorator(f: Callable[..., None]) -> Callable[..., None]:
self.add_method(method_name, f, background=True, category=category,
desc=desc, long_desc=long_desc,
deprecated=deprecated)
deprecated=deprecated, clnrest_data=clnrest_data)
return f
return decorator

def method(self, method_name: str, category: Optional[str] = None,
desc: Optional[str] = None,
long_desc: Optional[str] = None,
deprecated: Union[bool, List[str]] = None) -> JsonDecoratorType:
deprecated: Union[bool, List[str]] = None,
clnrest_data: Optional[CLNRestData] = None) -> JsonDecoratorType:
"""Decorator to add a plugin method to the dispatch table.

Internally uses add_method.
Expand All @@ -518,7 +528,8 @@ def decorator(f: Callable[..., JSONType]) -> Callable[..., JSONType]:
category=category,
desc=desc,
long_desc=long_desc,
deprecated=deprecated)
deprecated=deprecated,
clnrest_data=clnrest_data)
return f
return decorator

Expand Down Expand Up @@ -964,6 +975,9 @@ def _getmanifest(self, **kwargs) -> JSONType:
if method.long_desc:
m = methods[len(methods) - 1]
m["long_description"] = method.long_desc
if method.clnrest:
m = methods[len(methods) - 1]
m["clnrest"] = method.clnrest

manifest = {
'options': list(d.json() for d in self.options.values()),
Expand Down
218 changes: 110 additions & 108 deletions contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions contrib/pyln-testing/pyln/testing/grpc2py.py
Original file line number Diff line number Diff line change
Expand Up @@ -2636,6 +2636,15 @@ def stop2py(m):
})


def help_help_clnrest2py(m):
return remove_default({
"content_type": m.content_type, # PrimitiveField in generate_composite
"method": m.method, # PrimitiveField in generate_composite
"path": m.path, # PrimitiveField in generate_composite
"rune": m.rune, # PrimitiveField in generate_composite
})


def help_help2py(m):
return remove_default({
"command": m.command, # PrimitiveField in generate_composite
Expand Down
28 changes: 28 additions & 0 deletions doc/schemas/lightning-help.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,34 @@
"description": [
"The command."
]
},
"clnrest": {
"type": "object",
"additionalProperties": false,
"required": ["path", "method", "content_type", "rune"],
"added": "v24.08",
"properties": {
"path": {
"type": "string",
"description": "the path to the HTTP endpoint for this command",
"added": "v24.08"
},
"method": {
"type": "string",
"description": "the HTTP method for this command",
"added": "v24.08"
},
"content_type": {
"type": "string",
"description": "http content-type that clnrest should return",
"added": "v24.08"
},
"rune": {
"type": "boolean",
"description": "whether or not this command requires a rune for authentication",
"added": "v24.08"
}
}
}
}
}
Expand Down
54 changes: 48 additions & 6 deletions lightningd/jsonrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,23 @@ static void json_add_help_command(struct command *cmd,
json_command->name));
json_object_start(response, NULL);
json_add_string(response, "command", usage);

if (json_command->clnrest) {
json_object_start(response, "clnrest");
if (json_command->clnrest->method) {
json_add_string(response, "method", json_command->clnrest->method);
}
if (json_command->clnrest->path) {
json_add_string(response, "path", json_command->clnrest->path);
}
if (json_command->clnrest->content_type) {
json_add_string(response, "content_type", json_command->clnrest->content_type);
}
if (json_command->clnrest->rune) {
json_add_bool(response, "rune", *json_command->clnrest->rune);
}
json_object_end(response);
}
json_object_end(response);
}

Expand Down Expand Up @@ -1357,14 +1374,39 @@ static void destroy_json_command(struct json_command *command, struct jsonrpc *r
abort();
}

static bool command_add(struct jsonrpc *rpc, struct json_command *command)
static bool command_add(struct jsonrpc *rpc, struct json_command *command,
char **collision_name)
{
size_t count = tal_count(rpc->commands);

/* Check that we don't clobber a method */
for (size_t i = 0; i < count; i++)
if (streq(rpc->commands[i]->name, command->name))
for (size_t i = 0; i < count; i++) {

if (streq(rpc->commands[i]->name, command->name)) {
if (collision_name) {
*collision_name =
tal_strdup(tmpctx, rpc->commands[i]->name);
}
return false;
}

/* Check for clnrest conflict */
if (command->clnrest && rpc->commands[i]->clnrest) {
bool method_match =
streq(command->clnrest->method,
rpc->commands[i]->clnrest->method);
bool path_match =
streq(command->clnrest->path,
rpc->commands[i]->clnrest->path);
if (method_match && path_match) {
if (collision_name) {
*collision_name = tal_strdup(
tmpctx, rpc->commands[i]->name);
}
return false;
}
}
}

tal_arr_expand(&rpc->commands, command);
return true;
Expand All @@ -1388,9 +1430,9 @@ static void setup_command_usage(struct lightningd *ld,
}

bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command,
const char *usage TAKES)
const char *usage TAKES, char **collision_name)
{
if (!command_add(rpc, command))
if (!command_add(rpc, command, collision_name))
return false;
usage = tal_strdup(command, usage);
strmap_add(&rpc->usagemap, command->name, usage);
Expand All @@ -1402,7 +1444,7 @@ static bool jsonrpc_command_add_perm(struct lightningd *ld,
struct jsonrpc *rpc,
struct json_command *command)
{
if (!command_add(rpc, command))
if (!command_add(rpc, command, NULL))
return false;
setup_command_usage(ld, command);
return true;
Expand Down
Loading