From 1fa562667286c3cee2988085c3774af8e4893b38 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 1 Jun 2020 14:12:18 -0700 Subject: [PATCH] Fix README for style (#53) Add an example with content from the readme. This improves the score on pub and lets me run the analyzer over the example. - Remove the import prefix for the example, it is especially unwieldy for the `Parameters` type annotation. - Add argument types to the callbacks to avoid implicit `dynamic` which makes it harder to use inside the method body. - Change `[]` inside comments to backticks. `[]` is only used for doc comments. - Drop the `getNamed` API that has not been present since the very first code review for this package. - Use lower camel case for a constant. - Use single quotes consistently. There was a mix before, this is especially important for imports which are idiomatically written with single quotes. - Add a note about message buffering before `listen`. - Use `WebSocketChannel.connect` for platform agnostic imports. - Use `async/await` in the client example. --- CHANGELOG.md | 2 + README.md | 100 ++++++++++++++++++++++---------------------- example/client.dart | 40 ++++++++++++++++++ example/main.dart | 70 +++++++++++++++++++++++++++++++ pubspec.yaml | 3 +- 5 files changed, 165 insertions(+), 50 deletions(-) create mode 100644 example/client.dart create mode 100644 example/main.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index c3eec9a..11e0cc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.2.1-dev + ## 2.2.0 * Added `strictProtocolChecks` named parameter to `Server` and `Peer` diff --git a/README.md b/README.md index 0567d20..64562ac 100644 --- a/README.md +++ b/README.md @@ -8,71 +8,70 @@ A JSON-RPC 2.0 server exposes a set of methods that can be called by clients. These methods can be registered using `Server.registerMethod`: ```dart -import "package:json_rpc_2/json_rpc_2.dart" as json_rpc; -import "package:stream_channel/stream_channel.dart"; -import "package:web_socket_channel/io.dart"; +import 'package:json_rpc_2/json_rpc_2.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; -main() async { - var socket = IOWebSocketChannel.connect('ws://localhost:4321'); +void main() { + var socket = WebSocketChannel.connect(Uri.parse('ws://localhost:4321')); - // The socket is a StreamChannel because it might emit binary - // Lists, but JSON RPC 2 only works with Strings so we assert it only + // The socket is a `StreamChannel` because it might emit binary + // `List`, but JSON RPC 2 only works with Strings so we assert it only // emits those by casting it. - var server = new json_rpc.Server(socket.cast()); + var server = Server(socket.cast()); // Any string may be used as a method name. JSON-RPC 2.0 methods are // case-sensitive. var i = 0; - server.registerMethod("count", () { + server.registerMethod('count', () { // Just return the value to be sent as a response to the client. This can // be anything JSON-serializable, or a Future that completes to something // JSON-serializable. return i++; }); - // Methods can take parameters. They're presented as a [Parameters] object + // Methods can take parameters. They're presented as a `Parameters` object // which makes it easy to validate that the expected parameters exist. - server.registerMethod("echo", (params) { - // If the request doesn't have a "message" parameter, this will + server.registerMethod('echo', (Parameters params) { + // If the request doesn't have a "message" parameter this will // automatically send a response notifying the client that the request // was invalid. - return params.getNamed("message"); + return params['message'].value; }); - // [Parameters] has methods for verifying argument types. - server.registerMethod("subtract", (params) { + // `Parameters` has methods for verifying argument types. + server.registerMethod('subtract', (Parameters params) { // If "minuend" or "subtrahend" aren't numbers, this will reject the // request. - return params.getNum("minuend") - params.getNum("subtrahend"); + return params['minuend'].asNum - params['subtrahend'].asNum; }); // [Parameters] also supports optional arguments. - server.registerMethod("sort", (params) { - var list = params.getList("list"); + server.registerMethod('sort', (Parameters params) { + var list = params['list'].asList; list.sort(); - if (params.getBool("descending", orElse: () => false)) { - return params.list.reversed; + if (params['descendint'].asBoolOr(false)) { + return list.reversed; } else { - return params.list; + return list; } }); - // A method can send an error response by throwing a - // `json_rpc.RpcException`. Any positive number may be used as an - // application- defined error code. - const DIVIDE_BY_ZERO = 1; - server.registerMethod("divide", (params) { - var divisor = params.getNum("divisor"); + // A method can send an error response by throwing a `RpcException`. + // Any positive number may be used as an application- defined error code. + const dividByZero = 1; + server.registerMethod('divide', (Parameters params) { + var divisor = params['divisor'].asNum; if (divisor == 0) { - throw new json_rpc.RpcException( - DIVIDE_BY_ZERO, "Cannot divide by zero."); + throw RpcException(dividByZero, 'Cannot divide by zero.'); } - return params.getNum("dividend") / divisor; + return params['dividend'].asNum / divisor; }); - // To give you time to register all your methods, the server won't actually - // start listening for requests until you call `listen`. + // To give you time to register all your methods, the server won't start + // listening for requests until you call `listen`. Messages are buffered until + // listen is called. The returned Future won't complete until the connection + // is closed. server.listen(); } ``` @@ -84,38 +83,41 @@ responses to those method calls. These methods can be called using `Client.sendRequest`: ```dart -import "package:json_rpc_2/json_rpc_2.dart" as json_rpc; -import "package:stream_channel/stream_channel.dart"; -import "package:web_socket_channel/html.dart"; +import 'package:json_rpc_2/json_rpc_2.dart'; +import 'package:pedantic/pedantic.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; -main() async { - var socket = HtmlWebSocketChannel.connect('ws://localhost:4321'); - var client = new json_rpc.Client(socket); +void main() async { + var socket = WebSocketChannel.connect(Uri.parse('ws://localhost:4321')); + var client = Client(socket.cast()); + + // The client won't subscribe to the input stream until you call `listen`. + // The returned Future won't complete until the connection is closed. + unawaited(client.listen()); // This calls the "count" method on the server. A Future is returned that // will complete to the value contained in the server's response. - client.sendRequest("count").then((result) => print("Count is $result.")); + var count = await client.sendRequest('count'); + print('Count is $count'); // Parameters are passed as a simple Map or, for positional parameters, an // Iterable. Make sure they're JSON-serializable! - client.sendRequest("echo", {"message": "hello"}) - .then((echo) => print('Echo says "$echo"!')); + var echo = await client.sendRequest('echo', {'message': 'hello'}); + print('Echo says "$echo"!'); // A notification is a way to call a method that tells the server that no // result is expected. Its return type is `void`; even if it causes an // error, you won't hear back. - client.sendNotification("count"); + client.sendNotification('count'); // If the server sends an error response, the returned Future will complete // with an RpcException. You can catch this error and inspect its error // code, message, and any data that the server sent along with it. - client.sendRequest("divide", {"dividend": 2, "divisor": 0}) - .catchError((error) { - print("RPC error ${error.code}: ${error.message}"); - }); - - // The client won't subscribe to the input stream until you call `listen`. - client.listen(); + try { + await client.sendRequest('divide', {'dividend': 2, 'divisor': 0}); + } on RpcException catch (error) { + print('RPC error ${error.code}: ${error.message}'); + } } ``` diff --git a/example/client.dart b/example/client.dart new file mode 100644 index 0000000..243b83e --- /dev/null +++ b/example/client.dart @@ -0,0 +1,40 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:json_rpc_2/json_rpc_2.dart'; +import 'package:pedantic/pedantic.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +void main() async { + var socket = WebSocketChannel.connect(Uri.parse('ws://localhost:4321')); + var client = Client(socket.cast()); + + // The client won't subscribe to the input stream until you call `listen`. + // The returned Future won't complete until the connection is closed. + unawaited(client.listen()); + + // This calls the "count" method on the server. A Future is returned that + // will complete to the value contained in the server's response. + var count = await client.sendRequest('count'); + print('Count is $count'); + + // Parameters are passed as a simple Map or, for positional parameters, an + // Iterable. Make sure they're JSON-serializable! + var echo = await client.sendRequest('echo', {'message': 'hello'}); + print('Echo says "$echo"!'); + + // A notification is a way to call a method that tells the server that no + // result is expected. Its return type is `void`; even if it causes an + // error, you won't hear back. + client.sendNotification('count'); + + // If the server sends an error response, the returned Future will complete + // with an RpcException. You can catch this error and inspect its error + // code, message, and any data that the server sent along with it. + try { + await client.sendRequest('divide', {'dividend': 2, 'divisor': 0}); + } on RpcException catch (error) { + print('RPC error ${error.code}: ${error.message}'); + } +} diff --git a/example/main.dart b/example/main.dart new file mode 100644 index 0000000..fc6042e --- /dev/null +++ b/example/main.dart @@ -0,0 +1,70 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:json_rpc_2/json_rpc_2.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +void main() { + var socket = WebSocketChannel.connect(Uri.parse('ws://localhost:4321')); + + // The socket is a `StreamChannel` because it might emit binary + // `List`, but JSON RPC 2 only works with Strings so we assert it only + // emits those by casting it. + var server = Server(socket.cast()); + + // Any string may be used as a method name. JSON-RPC 2.0 methods are + // case-sensitive. + var i = 0; + server.registerMethod('count', () { + // Just return the value to be sent as a response to the client. This can + // be anything JSON-serializable, or a Future that completes to something + // JSON-serializable. + return i++; + }); + + // Methods can take parameters. They're presented as a `Parameters` object + // which makes it easy to validate that the expected parameters exist. + server.registerMethod('echo', (Parameters params) { + // If the request doesn't have a "message" parameter this will + // automatically send a response notifying the client that the request + // was invalid. + return params['message'].value; + }); + + // `Parameters` has methods for verifying argument types. + server.registerMethod('subtract', (Parameters params) { + // If "minuend" or "subtrahend" aren't numbers, this will reject the + // request. + return params['minuend'].asNum - params['subtrahend'].asNum; + }); + + // [Parameters] also supports optional arguments. + server.registerMethod('sort', (Parameters params) { + var list = params['list'].asList; + list.sort(); + if (params['descendint'].asBoolOr(false)) { + return list.reversed; + } else { + return list; + } + }); + + // A method can send an error response by throwing a `RpcException`. + // Any positive number may be used as an application- defined error code. + const dividByZero = 1; + server.registerMethod('divide', (Parameters params) { + var divisor = params['divisor'].asNum; + if (divisor == 0) { + throw RpcException(dividByZero, 'Cannot divide by zero.'); + } + + return params['dividend'].asNum / divisor; + }); + + // To give you time to register all your methods, the server won't start + // listening for requests until you call `listen`. Messages are buffered until + // listen is called. The returned Future won't complete until the connection + // is closed. + server.listen(); +} diff --git a/pubspec.yaml b/pubspec.yaml index abd2520..d030b6b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: json_rpc_2 -version: 2.2.0 +version: 2.2.1-dev description: >- Utilities to write a client or server using the JSON-RPC 2.0 spec. homepage: https://github.com/dart-lang/json_rpc_2 @@ -14,3 +14,4 @@ dependencies: dev_dependencies: pedantic: ^1.8.0 test: ^1.0.0 + web_socket_channel: ^1.1.0