Skip to content
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

feat: scalar delete #43

Merged
merged 10 commits into from
Jan 3, 2024
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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an issue captured in #36

// _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,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

header was misnamed

}));

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