Skip to content

Commit

Permalink
feat: scalar delete (#43)
Browse files Browse the repository at this point in the history
* feat: add scalar delete

* chore: add docstrings to scalar set and get response classes

* add validators and export more things and dart format

* add validators to control plane operations

* fix: add missing grpc error to sdk exception function

* fix: make sure to delete test caches

* chore: add tests for scalar get, set, delete

* fix imports and dart format

* remove unnecessary examples folder bc it was renamed
  • Loading branch information
anitarua committed Jan 3, 2024
1 parent 219c50f commit 2937864
Show file tree
Hide file tree
Showing 13 changed files with 362 additions and 42 deletions.
3 changes: 3 additions & 0 deletions lib/momento.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ export 'src/config/logger.dart' show LogLevel;
export 'src/messages/responses/cache/control/create_cache_response.dart';
export 'src/messages/responses/cache/control/delete_cache_response.dart';
export 'src/messages/responses/cache/control/list_caches_response.dart';
export 'src/messages/responses/cache/data/scalar/delete_response.dart';
export 'src/messages/responses/cache/data/scalar/get_response.dart';
export 'src/messages/responses/cache/data/scalar/set_response.dart';

// TODO: Export any libraries intended for clients of this package.
72 changes: 64 additions & 8 deletions lib/src/cache_client.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'package:momento/momento.dart';
import 'package:momento/src/config/cache_configuration.dart';
import 'package:momento/src/errors/errors.dart';
import 'package:momento/src/internal/control_client.dart';
import 'package:momento/src/internal/data_client.dart';
import 'package:momento/src/messages/responses/cache/data/scalar/get_response.dart';
import 'package:momento/src/messages/responses/cache/data/scalar/set_response.dart';
import 'package:logging/logging.dart';
import 'package:momento/src/internal/utils/validators.dart';

// import 'config/logger.dart';

abstract class ICacheClient {
Expand All @@ -14,10 +15,12 @@ abstract class ICacheClient {

Future<ListCachesResponse> listCaches();

Future<GetResponse> get(String cacheName, Value value);
Future<GetResponse> get(String cacheName, Value key);

Future<SetResponse> set(String cacheName, Value key, Value value,
{Duration? ttl});

Future<DeleteResponse> delete(String cacheName, Value key);
}

class CacheClient implements ICacheClient {
Expand All @@ -29,36 +32,89 @@ class CacheClient implements ICacheClient {
CacheConfiguration configuration, Duration defaultTtl) {
_dataClient = DataClient(credentialProvider, configuration, defaultTtl);
_controlClient = ControlClient(credentialProvider, configuration);
// TODO: fix logging level issue
// _logger.level = determineLoggerLevel(configuration.logLevel);
_logger.finest("initializing cache client");
}

@override
Future<CreateCacheResponse> createCache(String cacheName) {
// TODO: add validators
try {
validateCacheName(cacheName);
} catch (e) {
if (e is SdkException) {
return Future.value(CreateCacheError(e));
} else {
return Future.value(CreateCacheError(
UnknownException("Unexpected error: $e", null, null)));
}
}
return _controlClient.createCache(cacheName);
}

@override
Future<DeleteCacheResponse> deleteCache(String cacheName) {
// TODO: add validators
try {
validateCacheName(cacheName);
} catch (e) {
if (e is SdkException) {
return Future.value(DeleteCacheError(e));
} else {
return Future.value(DeleteCacheError(
UnknownException("Unexpected error: $e", null, null)));
}
}
return _controlClient.deleteCache(cacheName);
}

@override
Future<ListCachesResponse> listCaches() {
// TODO: add validators
return _controlClient.listCaches();
}

@override
Future<GetResponse> get(String cacheName, Value value) {
return _dataClient.get(cacheName, value);
Future<GetResponse> get(String cacheName, Value key) {
try {
validateCacheName(cacheName);
} catch (e) {
if (e is SdkException) {
return Future.value(GetError(e));
} else {
return Future.value(
GetError(UnknownException("Unexpected error: $e", null, null)));
}
}
return _dataClient.get(cacheName, key);
}

@override
Future<SetResponse> set(String cacheName, Value key, Value value,
{Duration? ttl}) {
try {
validateCacheName(cacheName);
} catch (e) {
if (e is SdkException) {
return Future.value(SetError(e));
} else {
return Future.value(
SetError(UnknownException("Unexpected error: $e", null, null)));
}
}
return _dataClient.set(cacheName, key, value, ttl: ttl);
}

@override
Future<DeleteResponse> delete(String cacheName, Value key) {
try {
validateCacheName(cacheName);
} catch (e) {
if (e is SdkException) {
return Future.value(DeleteError(e));
} else {
return Future.value(
DeleteError(UnknownException("Unexpected error: $e", null, null)));
}
}
return _dataClient.delete(cacheName, key);
}
}
49 changes: 45 additions & 4 deletions lib/src/errors/errors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ class MomentoErrorTransportDetails {
}

class MomentoGrpcErrorDetails {
final Code _code;
final String _details;
final int _code;
final Object? _details;
final GrpcMetadata? _metadata;

MomentoGrpcErrorDetails(this._code, this._details, this._metadata);

Code get code => _code;
String get details => _details;
int get code => _code;
Object? get details => _details;
GrpcMetadata? get metadata => _metadata;
}

Expand Down Expand Up @@ -262,3 +262,44 @@ class IllegalArgumentError extends Error {
String message;
IllegalArgumentError(this.message) : super();
}

SdkException grpcStatusToSdkException(GrpcError grpcError) {
final message = "${grpcError.message}";
final transportDetails = MomentoErrorTransportDetails(
MomentoGrpcErrorDetails(grpcError.code, grpcError.rawResponse, null));
switch (grpcError.code) {
case StatusCode.aborted:
return InternalServerException(message, grpcError, transportDetails);
case StatusCode.alreadyExists:
return AlreadyExistsException(message, grpcError, transportDetails);
case StatusCode.cancelled:
return CancelledException(message, grpcError, transportDetails);
case StatusCode.dataLoss:
return InternalServerException(message, grpcError, transportDetails);
case StatusCode.deadlineExceeded:
return TimeoutException(message, grpcError, transportDetails);
case StatusCode.failedPrecondition:
return FailedPreconditionException(message, grpcError, transportDetails);
case StatusCode.internal:
return InternalServerException(message, grpcError, transportDetails);
case StatusCode.invalidArgument:
return InvalidArgumentException(message, grpcError, transportDetails);
case StatusCode.notFound:
return NotFoundException(message, grpcError, transportDetails);
case StatusCode.outOfRange:
return BadRequestException(message, grpcError, transportDetails);
case StatusCode.permissionDenied:
return PermissionException(message, grpcError, transportDetails);
case StatusCode.resourceExhausted:
return ClientResourceExhaustedException(
message, grpcError, transportDetails);
case StatusCode.unauthenticated:
return AuthenticationException(message, grpcError, transportDetails);
case StatusCode.unimplemented:
return BadRequestException(message, grpcError, transportDetails);
case StatusCode.unknown:
return UnknownException(message, grpcError, transportDetails);
default:
return UnknownException(message, grpcError, transportDetails);
}
}
12 changes: 6 additions & 6 deletions lib/src/internal/control_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class ControlClient implements AbstractControlClient {
} catch (e) {
if (e is GrpcError && e.code == StatusCode.alreadyExists) {
return AlreadyExists();
} else if (e is SdkException) {
return CreateCacheError(e);
} else if (e is GrpcError) {
return CreateCacheError(grpcStatusToSdkException(e));
} else {
return CreateCacheError(
UnknownException("Unexpected error: $e", null, null));
Expand All @@ -59,8 +59,8 @@ class ControlClient implements AbstractControlClient {
}));
return DeleteCacheSuccess();
} catch (e) {
if (e is SdkException) {
return DeleteCacheError(e);
if (e is GrpcError) {
return DeleteCacheError(grpcStatusToSdkException(e));
} else {
return DeleteCacheError(
UnknownException("Unexpected error: $e", null, null));
Expand All @@ -75,8 +75,8 @@ class ControlClient implements AbstractControlClient {
final resp = await _client.listCaches(request);
return ListCachesSuccess(resp.cache);
} catch (e) {
if (e is SdkException) {
return ListCachesError(e);
if (e is GrpcError) {
return ListCachesError(grpcStatusToSdkException(e));
} else {
return ListCachesError(
UnknownException("Unexpected error: $e", null, null));
Expand Down
45 changes: 31 additions & 14 deletions lib/src/internal/data_client.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import 'package:momento/generated/cacheclient.pbgrpc.dart';
import 'package:momento/momento.dart';
import 'package:momento/src/config/cache_configuration.dart';
import 'package:momento/src/errors/errors.dart';
import 'package:momento/src/messages/responses/cache/data/scalar/get_response.dart';
import 'package:momento/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<GetResponse> get(String cacheName, Value key);

Future<SetResponse> set(String cacheName, Value key, Value value,
{Duration? ttl});

Future<DeleteResponse> delete(String cacheName, Value key);
}

class DataClient implements AbstractDataClient {
Expand All @@ -39,7 +37,7 @@ class DataClient implements AbstractDataClient {
try {
var resp = await _client.get(request,
options: CallOptions(metadata: {
'cacheName': cacheName,
'cache': cacheName,
}));

switch (resp.result) {
Expand All @@ -52,8 +50,8 @@ class DataClient implements AbstractDataClient {
"unknown cache get error ${resp.result}", null, null));
}
} catch (e) {
if (e is SdkException) {
return GetError(e);
if (e is GrpcError) {
return GetError(grpcStatusToSdkException(e));
} else {
return GetError(UnknownException("Unexpected error: $e", null, null));
}
Expand All @@ -66,21 +64,40 @@ class DataClient implements AbstractDataClient {
var request = SetRequest_();
request.cacheKey = key.toBinary();
request.cacheBody = value.toBinary();
request.ttlMilliseconds = (ttl != null
? ttl.inMilliseconds
: _defaultTtl.inMilliseconds) as Int64;
request.ttlMilliseconds =
Int64(ttl != null ? ttl.inMilliseconds : _defaultTtl.inMilliseconds);
try {
await _client.set(request,
options: CallOptions(metadata: {
'cacheName': cacheName,
'cache': cacheName,
}));
return SetSuccess();
} catch (e) {
if (e is SdkException) {
return SetError(e);
if (e is GrpcError) {
return SetError(grpcStatusToSdkException(e));
} else {
return SetError(UnknownException("Unexpected error: $e", null, null));
}
}
}

@override
Future<DeleteResponse> delete(String cacheName, Value key) async {
var request = DeleteRequest_();
request.cacheKey = key.toBinary();
try {
await _client.delete(request,
options: CallOptions(metadata: {
'cache': cacheName,
}));
return DeleteSuccess();
} catch (e) {
if (e is GrpcError) {
return DeleteError(grpcStatusToSdkException(e));
} else {
return DeleteError(
UnknownException("Unexpected error: $e", null, null));
}
}
}
}
9 changes: 4 additions & 5 deletions lib/src/internal/pubsub_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ class ClientPubsub implements AbstractPubsubClient {
timeout: _configuration.transportStrategy.grpcConfig.deadline));
return TopicPublishSuccess();
} catch (e) {
if (e is SdkException) {
return TopicPublishError(e);
if (e is GrpcError) {
return TopicPublishError(grpcStatusToSdkException(e));
}
return TopicPublishError(
UnknownException("Unexpected error: $e", null, null));
Expand All @@ -73,9 +73,8 @@ class ClientPubsub implements AbstractPubsubClient {
return TopicSubscription(stream, request.resumeAtTopicSequenceNumber,
this, cacheName, topicName);
} catch (e) {
print("Error in pubsubclient.subscribe: $e");
if (e is SdkException) {
return TopicSubscribeError(e);
if (e is GrpcError) {
return TopicSubscribeError(grpcStatusToSdkException(e));
}
return TopicSubscribeError(
UnknownException("Unexpected error: $e", null, null));
Expand Down
13 changes: 13 additions & 0 deletions lib/src/internal/utils/validators.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:momento/src/errors/errors.dart';

void _validateString(String str, String errorMessage) {
if (str.trim().isEmpty) {
throw InvalidArgumentException(errorMessage, null, null);
}
}

void validateCacheName(String cacheName) =>
_validateString(cacheName, "Invalid cache name");

void validateTopicName(String topicName) =>
_validateString(topicName, "Invalid topic name");
27 changes: 27 additions & 0 deletions lib/src/messages/responses/cache/data/scalar/delete_response.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:momento/src/messages/responses/responses_base.dart';

/// Sealed class for a delete cache item request.
///
/// Pattern matching can be used to operate on the appropriate subtype.
/// ```dart
/// switch (response) {
/// case DeleteSuccess():
/// // handle success
/// case DeleteError():
/// // handle error
/// }
/// ```
sealed class DeleteResponse {}

/// Indicates a successful delete cache item request.
class DeleteSuccess implements DeleteResponse {}

/// Indicates that an error occurred during the delete cache item request.
///
/// The response object includes the following fields you can use to determine how you want to handle the error:
/// - `errorCode`: a unique Momento error code indicating the type of error that occurred
/// - `message`: a human-readable description of the error
/// - `innerException`: the original error that caused the failure; can be re-thrown
class DeleteError extends ErrorResponseBase implements DeleteResponse {
DeleteError(super.exception);
}
Loading

0 comments on commit 2937864

Please sign in to comment.