| // Copyright (c) 2023, 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 'dart:async'; |
| import 'dart:convert'; |
| |
| import 'package:dtd/dtd.dart'; |
| import 'package:json_rpc_2/json_rpc_2.dart'; |
| |
| /// To run this example pass the DTD uri as a parameter: |
| /// |
| /// Example: |
| /// ```sh |
| /// dart run dtd_service_example.dart ws://127.0.0.1:62925/cKB5QFiAUNMzSzlb |
| /// ``` |
| void main(List<String> args) async { |
| final dtdUrl = args[0]; // pass the url as a param to the example |
| |
| DartToolingDaemon? clientA; |
| DartToolingDaemon? clientB; |
| |
| final serviceRegisteredCompleted = Completer<void>(); |
| final serviceUnregisteredCompleted = Completer<void>(); |
| |
| try { |
| // Set up the services that will be used to show service method |
| // interactions. |
| clientA = await DartToolingDaemon.connect(Uri.parse(dtdUrl)); |
| clientB = await DartToolingDaemon.connect(Uri.parse(dtdUrl)); |
| |
| // Monitor events that come to the second client over the Service |
| // stream. |
| clientB.onEvent('Service').listen((e) { |
| switch (e.kind) { |
| case 'ServiceRegistered': |
| serviceRegisteredCompleted.complete(); |
| case 'ServiceUnregistered': |
| serviceUnregisteredCompleted.complete(); |
| } |
| print(jsonEncode({'stream': e.stream, 'kind': e.kind, 'data': e.data})); |
| }); |
| await clientB.streamListen('Service'); |
| |
| // Register the ExampleServer.getServerState service method on the first |
| // client so that other other clients can call it. |
| await clientA.registerService( |
| 'ExampleServer', |
| 'getServerState', |
| (params) async { |
| // This callback is what will be run when clients call |
| // ExampleServer.getServerState. |
| final getStateRequest = GetStateRequest.fromParams(params); |
| |
| final duration = const Duration(minutes: 45); |
| final status = |
| getStateRequest.verbose ? 'The server is running' : 'Running'; |
| |
| return ExampleStateResponse(duration, status).toJson(); |
| }, |
| capabilities: { |
| 'supportsNewExamples': true, |
| }, |
| ); |
| |
| await serviceRegisteredCompleted.future; |
| |
| // Call the registered service from a different client. |
| final response = await clientB.getServerState(verbose: true); |
| |
| // The ExampleServerState response is now printed. |
| print(jsonEncode(response.toJson())); |
| } finally { |
| // Close the first client and wait for the unregistered event before closing |
| // the second client. |
| await clientA?.close(); |
| await serviceUnregisteredCompleted.future; |
| await clientB?.close(); |
| } |
| } |
| |
| /// A helper class used to simplify passing and receiving json parameters to |
| /// ExampleServer.getServerState. |
| class GetStateRequest { |
| final bool verbose; |
| static const String _kVerbose = 'verbose'; |
| GetStateRequest(this.verbose); |
| factory GetStateRequest.fromParams(Parameters parameters) { |
| return GetStateRequest(parameters[_kVerbose].asBool); |
| } |
| |
| Map<String, Object> toJson() => <String, Object>{ |
| _kVerbose: verbose, |
| }; |
| } |
| |
| /// A helper class used to simplify passing and receiving json results in the |
| /// ExampleServer.getServerState response. |
| class ExampleStateResponse { |
| late Duration uptime; |
| late String status; |
| |
| static String get type => 'ExampleStateResponse'; |
| |
| static const String _kUptime = 'uptime'; |
| static const String _kStatus = 'status'; |
| |
| ExampleStateResponse(this.uptime, this.status); |
| |
| ExampleStateResponse._fromDTDResponse(DTDResponse response) |
| : uptime = Duration( |
| milliseconds: response.result[_kUptime] as int, |
| ), |
| status = response.result[_kStatus] as String; |
| |
| factory ExampleStateResponse.fromDTDResponse(DTDResponse response) { |
| // Ensure that the response has the type you expect. |
| if (response.type != type) { |
| throw RpcException.invalidParams( |
| 'Expected DTDResponse.type to be $type, got: ${response.type}', |
| ); |
| } |
| return ExampleStateResponse._fromDTDResponse(response); |
| } |
| |
| Map<String, Object?> toJson() => <String, Object?>{ |
| 'type': type, |
| _kStatus: status, |
| _kUptime: uptime.inMilliseconds, |
| }; |
| |
| @override |
| String toString() { |
| return '$type(status:$status, uptime:$uptime)'; |
| } |
| } |
| |
| /// Adds the [getServerState] method to [DartToolingDaemon], so that calling the |
| /// ExampleServer.getServerState service method can be wrapped nicely behind a |
| /// method call from a given client. |
| extension ExampleExtension on DartToolingDaemon { |
| Future<ExampleStateResponse> getServerState({bool verbose = false}) async { |
| final result = await call( |
| 'ExampleServer', |
| 'getServerState', |
| params: GetStateRequest(verbose).toJson(), |
| ); |
| return ExampleStateResponse.fromDTDResponse(result); |
| } |
| } |