From a632ba95ed6ea3e1019185a46f10a502433a6d51 Mon Sep 17 00:00:00 2001 From: Matt Straathof <11823378+bruuuuuuuce@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:48:45 -0800 Subject: [PATCH] feat: add unary data client with get/set support (#35) --- lib/client_sdk_dart.dart | 1 + lib/src/cache_client.dart | 40 +++++++++ lib/src/internal/data_client.dart | 88 +++++++++++++++++++ .../cache/data/scalar/get_response.dart | 20 +++++ .../cache/data/scalar/set_response.dart | 9 ++ lib/src/messages/values.dart | 27 +++++- pubspec.yaml | 1 + 7 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 lib/src/cache_client.dart create mode 100644 lib/src/internal/data_client.dart create mode 100644 lib/src/messages/responses/cache/data/scalar/get_response.dart create mode 100644 lib/src/messages/responses/cache/data/scalar/set_response.dart diff --git a/lib/client_sdk_dart.dart b/lib/client_sdk_dart.dart index 26366a0..7b1241f 100644 --- a/lib/client_sdk_dart.dart +++ b/lib/client_sdk_dart.dart @@ -4,6 +4,7 @@ library; export 'src/topic_client.dart'; +export 'src/cache_client.dart'; export 'src/auth/credential_provider.dart'; export 'src/config/topic_configurations.dart'; export 'src/messages/values.dart'; diff --git a/lib/src/cache_client.dart b/lib/src/cache_client.dart new file mode 100644 index 0000000..daac88c --- /dev/null +++ b/lib/src/cache_client.dart @@ -0,0 +1,40 @@ +import 'package:client_sdk_dart/src/config/cache_configuration.dart'; +import 'package:client_sdk_dart/src/internal/data_client.dart'; +import 'package:client_sdk_dart/src/messages/responses/cache/data/scalar/get_response.dart'; +import 'package:client_sdk_dart/src/messages/responses/cache/data/scalar/set_response.dart'; +import 'package:logging/logging.dart'; + +import 'auth/credential_provider.dart'; +import 'config/logger.dart'; +import 'messages/values.dart'; + +abstract class ICacheClient { + Future get(String cacheName, Value value); + + Future set(String cacheName, Value key, Value value, + {Duration? ttl}); +} + +class CacheClient implements ICacheClient { + final DataClient _dataClient; + final Logger _logger = Logger('MomentoCacheClient'); + + CacheClient(CredentialProvider credentialProvider, + CacheConfiguration configuration, Duration defaultTtl) + : _dataClient = + DataClient(credentialProvider, configuration, defaultTtl) { + _logger.level = determineLoggerLevel(configuration.logLevel); + _logger.finest("initializing cache client"); + } + + @override + Future get(String cacheName, Value value) { + return _dataClient.get(cacheName, value); + } + + @override + Future set(String cacheName, Value key, Value value, + {Duration? ttl}) { + return _dataClient.set(cacheName, key, value, ttl: ttl); + } +} diff --git a/lib/src/internal/data_client.dart b/lib/src/internal/data_client.dart new file mode 100644 index 0000000..3f4338b --- /dev/null +++ b/lib/src/internal/data_client.dart @@ -0,0 +1,88 @@ +import 'package:client_sdk_dart/generated/cacheclient.pbgrpc.dart'; +import 'package:client_sdk_dart/src/config/cache_configuration.dart'; +import 'package:client_sdk_dart/src/errors/errors.dart'; +import 'package:client_sdk_dart/src/messages/responses/cache/data/scalar/get_response.dart'; +import 'package:client_sdk_dart/src/messages/responses/cache/data/scalar/set_response.dart'; +import 'package:fixnum/fixnum.dart'; +import 'package:grpc/grpc.dart'; + +import '../auth/credential_provider.dart'; +import '../messages/values.dart'; + +abstract class AbstractDataClient { + Future get(String cacheName, Value key); + + Future set(String cacheName, Value key, Value value, + {Duration? ttl}); +} + +class DataClient implements AbstractDataClient { + late ClientChannel _channel; + late ScsClient _client; + late CacheConfiguration _configuration; + late Duration _defaultTtl; + + DataClient(CredentialProvider credentialProvider, + CacheConfiguration configuration, Duration defaultTtl) { + _channel = ClientChannel(credentialProvider.cacheEndpoint); + _client = ScsClient(_channel, + options: CallOptions(metadata: { + 'authorization': credentialProvider.apiKey, + 'agent': 'dart:0.1.0', + }, timeout: _configuration.transportStrategy.grpcConfig.deadline)); + _configuration = configuration; + _defaultTtl = defaultTtl; + } + + @override + Future get(String cacheName, Value key) async { + var request = GetRequest_(); + request.cacheKey = key.toBinary(); + try { + var resp = await _client.get(request, + options: CallOptions(metadata: { + 'cacheName': cacheName, + })); + + switch (resp.result) { + case ECacheResult.Miss: + return GetMiss(); + case ECacheResult.Hit: + return GetHit(resp.cacheBody); + default: + return GetError(UnknownException( + "unknown cache get error ${resp.result}", null, null)); + } + } catch (e) { + if (e is SdkException) { + return GetError(e); + } else { + return GetError(UnknownException("Unexpected error: $e", null, null)); + } + } + } + + @override + Future set(String cacheName, Value key, Value value, + {Duration? ttl}) async { + var request = SetRequest_(); + request.cacheKey = key.toBinary(); + request.cacheBody = value.toBinary(); + request.ttlMilliseconds = (ttl != null + ? ttl.inMilliseconds + : _defaultTtl.inMilliseconds) as Int64; + try { + await _client.set(request, + options: CallOptions(metadata: { + 'cacheName': cacheName, + })); + return SetSuccess(); + } catch (e) { + if (e is SdkException) { + return SetError(e); + } else { + return SetError(UnknownException("Unexpected error: $e", null, null)); + } + } + } +} diff --git a/lib/src/messages/responses/cache/data/scalar/get_response.dart b/lib/src/messages/responses/cache/data/scalar/get_response.dart new file mode 100644 index 0000000..ea3abd6 --- /dev/null +++ b/lib/src/messages/responses/cache/data/scalar/get_response.dart @@ -0,0 +1,20 @@ +import 'dart:convert'; + +import 'package:client_sdk_dart/src/messages/responses/responses_base.dart'; + +sealed class GetResponse {} + +class GetMiss implements GetResponse {} + +class GetError extends ErrorResponseBase implements GetResponse { + GetError(super.exception); +} + +class GetHit implements GetResponse { + GetHit(this._value); + + final List _value; + + String get value => utf8.decode(_value); + List get binaryValue => _value; +} diff --git a/lib/src/messages/responses/cache/data/scalar/set_response.dart b/lib/src/messages/responses/cache/data/scalar/set_response.dart new file mode 100644 index 0000000..4a9c006 --- /dev/null +++ b/lib/src/messages/responses/cache/data/scalar/set_response.dart @@ -0,0 +1,9 @@ +import 'package:client_sdk_dart/src/messages/responses/responses_base.dart'; + +sealed class SetResponse {} + +class SetSuccess implements SetResponse {} + +class SetError extends ErrorResponseBase implements SetResponse { + SetError(super.exception); +} diff --git a/lib/src/messages/values.dart b/lib/src/messages/values.dart index 388df58..f833f04 100644 --- a/lib/src/messages/values.dart +++ b/lib/src/messages/values.dart @@ -1,13 +1,38 @@ -sealed class Value {} +import 'dart:convert'; + +sealed class Value { + List toBinary(); + String toUtf8(); +} class StringValue implements Value { final String _value; StringValue(String v) : _value = v; String get value => _value; + + @override + toBinary() { + return utf8.encode(_value); + } + + @override + toUtf8() { + return _value; + } } class BinaryValue implements Value { final List _value; BinaryValue(List v) : _value = v; List get value => _value; + + @override + List toBinary() { + return _value; + } + + @override + String toUtf8() { + return utf8.decode(_value); + } } diff --git a/pubspec.yaml b/pubspec.yaml index b40ae61..c08588a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,7 @@ environment: # Add regular dependencies here. dependencies: + fixnum: ^1.1.0 grpc: ^3.2.4 jwt_decoder: ^2.0.1 logging: ^1.2.0