-
Notifications
You must be signed in to change notification settings - Fork 32
Implement help <method-name> #300
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,38 +80,42 @@ void echoMethodWrongNumberOfArgs() throws IOException { | |
| assertEquals(expectedErrorCode, exception.jsonRpcCode); | ||
| } | ||
|
|
||
| @Test | ||
| void helpForHelpMethod() throws IOException { | ||
| var expectedResultSubstring = "Displays detailed help text for the specified method."; | ||
| try (var client = new DefaultRpcClient(endpoint, "", "")) { | ||
| String result = (String) client.send("help", "help"); | ||
| assertTrue(result.contains(expectedResultSubstring)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now we don't need to match the entire text of the result, but we should verify that the result contains the method name and the correct argument name (and optional parens) So that's three asserts per test:
|
||
| } | ||
| } | ||
|
|
||
| @Test | ||
| void helpMethod() throws IOException { | ||
| var expectedResult = """ | ||
| echo message | ||
| help | ||
| stop | ||
| """; | ||
| void helpForEchoMethod() throws IOException { | ||
| var expectedResultSubstring = "Returns the provided message exactly as it was sent."; | ||
| try (var client = new DefaultRpcClient(endpoint, "", "")) { | ||
| String result = (String) client.send("help"); | ||
| assertEquals(expectedResult, result); | ||
| String result = (String) client.send("help", "echo"); | ||
| assertTrue(result.contains(expectedResultSubstring)); | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * The help method is currently not fully implemented. It SHOULD allow | ||
| * for an argument, and only fail if the argument doesn't match an existing | ||
| * command. Once the help method is properly implemented we will need to change | ||
| * our tests | ||
| */ | ||
| @Test | ||
| void helpMethodOneArg() throws IOException { | ||
| int expectedErrorCode = JsonRpcError.Error.INVALID_PARAMS.getCode(); | ||
| var expectedErrorMessagePrefix = "Invalid params:"; | ||
| JsonRpcStatusException exception = | ||
| assertThrows(JsonRpcStatusException.class, () -> { | ||
| try (var client = new DefaultRpcClient(endpoint, "", "")) { | ||
| client.send("help", "echo"); | ||
| } | ||
| }); | ||
| assertTrue(exception.getMessage().startsWith(expectedErrorMessagePrefix)); | ||
| assertEquals(expectedErrorCode, exception.jsonRpcCode); | ||
| void helpForStopMethod() throws IOException { | ||
| var expectedResultSubstring = "Initiates the shutdown process of the JSON-RPC server."; | ||
| try (var client = new DefaultRpcClient(endpoint, "", "")) { | ||
| String result = (String) client.send("help", "stop"); | ||
| assertTrue(result.contains(expectedResultSubstring)); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| void helpMethodNoArg() throws IOException { | ||
liamgilligan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var expectedResult = "echo message\n" + | ||
| "help (method)\n" + | ||
| "stop "; | ||
| try (var client = new DefaultRpcClient(endpoint, "", "")) { | ||
| String result = (String) client.send("help"); | ||
| assertEquals(expectedResult, result); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /* | ||
| * Copyright 2014-2026 ConsensusJ Developers. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package org.consensusj.jsonrpc.help; | ||
|
|
||
| /** | ||
| * Recordlike object to store help information about a given command | ||
| */ | ||
| public class JsonRpcHelp { | ||
liamgilligan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| private final String summary; | ||
| private final String detail; | ||
|
|
||
| public JsonRpcHelp(String summary, String detail) { | ||
| this.summary = summary; | ||
| this.detail = detail; | ||
| } | ||
|
|
||
| /** | ||
| * @return The summary information for a command. | ||
| */ | ||
| public String summary() { | ||
| return summary; | ||
| } | ||
|
|
||
| /** | ||
| * @return The detailed information for a command. | ||
| */ | ||
| public String detail() { | ||
| return detail; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,18 +25,60 @@ | |
| import java.lang.invoke.MethodHandles; | ||
| import java.lang.reflect.Method; | ||
| import java.util.Map; | ||
| import org.consensusj.jsonrpc.help.JsonRpcHelp; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.function.Function; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| /** | ||
| * Simple Echo JSON-RPC Service | ||
| */ | ||
| public class EchoJsonRpcService extends AbstractJsonRpcService implements Closeable { | ||
| private static final Logger log = LoggerFactory.getLogger(EchoJsonRpcService.class); | ||
| private static final Map<String, Method> methods = JsonRpcServiceWrapper.reflect(MethodHandles.lookup().lookupClass()); | ||
| private static final String helpString = | ||
| "echo message\n" + | ||
| "help\n" + | ||
| "stop\n"; | ||
| private static final Map<String, JsonRpcHelp> helpMap = Map.of( | ||
liamgilligan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "echo", new JsonRpcHelp("message", | ||
| "Usage:\n" | ||
| + " echo <message>\n" | ||
| + "\n" | ||
| + "Description:\n" | ||
| + " Returns the provided message exactly as it was sent.\n" | ||
| + "\n" | ||
| + "Parameters:\n" | ||
| + " message (string, required)\n" | ||
| + " The text to be echoed back by the server.\n" | ||
| + "\n" | ||
| + "Example:\n" | ||
| + " echo \"hello world\"\n"), | ||
| "help", new JsonRpcHelp("(method)", | ||
| "Usage:\n" | ||
| + " help <method>\n" | ||
| + "\n" | ||
| + "Description:\n" | ||
| + " Displays detailed help text for the specified method.\n" | ||
| + " If the method name is not recognized or not given, a list of available\n" | ||
| + " methods and their parameters is returned instead.\n" | ||
| + "\n" | ||
| + "Parameters:\n" | ||
| + " method (string, optional)\n" | ||
| + " The name of the method to display help for.\n" | ||
| + "\n" | ||
| + "Example:\n" | ||
| + " help echo\n"), | ||
| "stop", new JsonRpcHelp("", | ||
| "Usage:\n" | ||
| + " stop\n" | ||
| + "\n" | ||
| + "Description:\n" | ||
| + " Initiates the shutdown process of the JSON-RPC server.\n" | ||
| + " The server will respond to this request before the\n" | ||
| + " shutdown completes, allowing the client to receive\n" | ||
| + " confirmation of the action.\n" | ||
| + "\n" | ||
| + "Parameters:\n" | ||
| + " None.\n") | ||
| ); | ||
| private static final String helpString = createHelpString(EchoJsonRpcService::createHelpStringLine); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This member should probably be renamed something like |
||
|
|
||
| private final JsonRpcShutdownService shutdownService; | ||
|
|
||
|
|
@@ -50,14 +92,31 @@ public void close() { | |
| log.info("Closing"); | ||
| } | ||
|
|
||
| /** | ||
| * Echo a given message back to the client. | ||
| * @param message: A string containing the message to be echoed | ||
| * @return A string containing the echoed message | ||
| */ | ||
| public CompletableFuture<String> echo(String message) { | ||
| log.debug("EchoJsonRpcService: echo {}", message); | ||
| return result(message); | ||
| } | ||
|
|
||
| public CompletableFuture<String> help() { | ||
| /** | ||
| * Get detailed help information for a given command. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... or all commands. |
||
| * @param method: A string containing the method name | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... or |
||
| * @return A string containing help information | ||
| */ | ||
| public CompletableFuture<String> help(String method) { | ||
| log.debug("EchoJsonRpcService: help"); | ||
| return result(helpString); | ||
| if (method == null) { | ||
| return result(helpString); | ||
| } | ||
| if (helpMap.containsKey(method)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would use an |
||
| return result(helpMap.get(method).detail()); | ||
| } else { | ||
| return result("Method not found.\n" + helpString); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -70,4 +129,26 @@ public CompletableFuture<String> stop() { | |
| String message = shutdownService.stopServer(); | ||
| return result(message); | ||
| } | ||
|
|
||
| /** | ||
| * Create a string containing the name of each method and it's parameters in alphabetical order. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No apostrophe on "it's" |
||
| * @param formatFunction A function determining how to format each line | ||
| * @return A string containing the name and parameters of each method | ||
| */ | ||
| private static String createHelpString(Function<Map.Entry<String, JsonRpcHelp>, String> formatFunction) { | ||
| return helpMap.entrySet().stream() | ||
| .sorted(Map.Entry.comparingByKey()) | ||
| .map(formatFunction) | ||
| .collect(Collectors.joining("\n")); | ||
| } | ||
|
|
||
| /** | ||
| * Default formatter. Appends the method's summary delimited with a space | ||
| * @param entry Map entry for a given method from `helpMap` | ||
| * @return Formatted line for `helpString` | ||
| */ | ||
| private static String createHelpStringLine(Map.Entry<String, JsonRpcHelp> entry) { | ||
| return entry.getKey() + " " + entry.getValue().summary(); | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
@BeforeEachto initializeclientfor each test. Use@AfterEachto callclient.close(). This will make the individual tests more readable by eliminating the call toDefaultRpcClientand thetryblock.