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: cache client control plane operations #44

Merged
merged 5 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ jobs:
# want to change this to 'flutter test'.
- name: Run tests
run: dart test
env:
TEST_API_KEY: ${{ secrets.ALPHA_TEST_AUTH_TOKEN }}
2 changes: 1 addition & 1 deletion examples/topics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ void main() async {

var topicClient = TopicClient(
CredentialProvider.fromEnvironmentVariable("MOMENTO_API_KEY"),
Mobile.latest());
MobileTopicConfiguration.latest());
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

names clashed when both topic and cache configs were exported from the top-level lib/client_sdk_dart.dart file


// start publishing messages in 2 seconds
Timer(const Duration(seconds: 2), () async {
Expand Down
4 changes: 4 additions & 0 deletions lib/client_sdk_dart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ export 'src/topic_client.dart';
export 'src/cache_client.dart';
export 'src/auth/credential_provider.dart';
export 'src/config/topic_configurations.dart';
export 'src/config/cache_configurations.dart';
export 'src/messages/values.dart';
export 'src/messages/responses/topics/topic_publish.dart';
export 'src/messages/responses/topics/topic_subscribe.dart';
export 'src/messages/responses/topics/topic_subscription_item.dart';
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';

// TODO: Export any libraries intended for clients of this package.
42 changes: 33 additions & 9 deletions lib/src/cache_client.dart
Original file line number Diff line number Diff line change
@@ -1,32 +1,56 @@
import 'package:client_sdk_dart/client_sdk_dart.dart';
import 'package:client_sdk_dart/src/config/cache_configuration.dart';
import 'package:client_sdk_dart/src/internal/control_client.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';
// import 'config/logger.dart';

abstract class ICacheClient {
Future<CreateCacheResponse> createCache(String cacheName);

Future<DeleteCacheResponse> deleteCache(String cacheName);

Future<ListCachesResponse> listCaches();

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

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

class CacheClient implements ICacheClient {
final DataClient _dataClient;
late final DataClient _dataClient;
late final ControlClient _controlClient;
final Logger _logger = Logger('MomentoCacheClient');

CacheClient(CredentialProvider credentialProvider,
CacheConfiguration configuration, Duration defaultTtl)
: _dataClient =
DataClient(credentialProvider, configuration, defaultTtl) {
_logger.level = determineLoggerLevel(configuration.logLevel);
CacheConfiguration configuration, Duration defaultTtl) {
_dataClient = DataClient(credentialProvider, configuration, defaultTtl);
_controlClient = ControlClient(credentialProvider, configuration);
// _logger.level = determineLoggerLevel(configuration.logLevel);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

issue captured in #36

_logger.finest("initializing cache client");
}

@override
Future<CreateCacheResponse> createCache(String cacheName) {
// TODO: add validators
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will be added in #43 which will also round out all the basic cache operations

return _controlClient.createCache(cacheName);
}

@override
Future<DeleteCacheResponse> deleteCache(String cacheName) {
// TODO: add validators
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);
Expand Down
2 changes: 1 addition & 1 deletion lib/src/config/cache_configurations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'cache_configuration.dart';
abstract interface class CacheClientConfigurations {}

/// Provides prebuilt configurations for the `CacheClient` on mobile platforms
class Mobile extends CacheClientConfigurations {
class MobileCacheConfiguration extends CacheClientConfigurations {
static CacheClientConfiguration defaultConfig() {
return latest();
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/config/topic_configurations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'transport/transport_strategy.dart';
abstract interface class TopicClientConfigurations {}

/// Provides prebuilt configurations for the `TopicClient` on mobile platforms
class Mobile extends TopicClientConfigurations {
class MobileTopicConfiguration extends TopicClientConfigurations {
static TopicClientConfiguration defaultConfig() {
return latest();
}
Expand Down
86 changes: 86 additions & 0 deletions lib/src/internal/control_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import 'package:client_sdk_dart/client_sdk_dart.dart';
import 'package:client_sdk_dart/generated/controlclient.pbgrpc.dart';
import 'package:client_sdk_dart/src/config/cache_configuration.dart';
import 'package:client_sdk_dart/src/errors/errors.dart';
import 'package:grpc/grpc.dart';

abstract class AbstractControlClient {
Future<CreateCacheResponse> createCache(String cacheName);

Future<DeleteCacheResponse> deleteCache(String cacheName);

Future<ListCachesResponse> listCaches();
}

class ControlClient implements AbstractControlClient {
late ClientChannel _channel;
late ScsControlClient _client;
final CacheConfiguration _configuration;

ControlClient(CredentialProvider credentialProvider, this._configuration) {
_channel = ClientChannel(credentialProvider.controlEndpoint);
_client = ScsControlClient(_channel,
options: CallOptions(metadata: {
'authorization': credentialProvider.apiKey,
'agent': 'dart:0.1.0',
}, timeout: _configuration.transportStrategy.grpcConfig.deadline));
}

@override
Future<CreateCacheResponse> createCache(String cacheName) async {
var request = CreateCacheRequest_();
request.cacheName = cacheName;
try {
await _client.createCache(request,
options: CallOptions(metadata: {
'cache': cacheName,
}));
return CreateCacheSuccess();
} catch (e) {
if (e is GrpcError && e.code == StatusCode.alreadyExists) {
return AlreadyExists();
} else if (e is SdkException) {
return CreateCacheError(e);
} else {
return CreateCacheError(
UnknownException("Unexpected error: $e", null, null));
}
}
}

@override
Future<DeleteCacheResponse> deleteCache(String cacheName) async {
var request = DeleteCacheRequest_();
request.cacheName = cacheName;
try {
await _client.deleteCache(request,
options: CallOptions(metadata: {
'cache': cacheName,
}));
return DeleteCacheSuccess();
} catch (e) {
if (e is SdkException) {
return DeleteCacheError(e);
} else {
return DeleteCacheError(
UnknownException("Unexpected error: $e", null, null));
}
}
}

@override
Future<ListCachesResponse> listCaches() async {
var request = ListCachesRequest_();
try {
final resp = await _client.listCaches(request);
return ListCachesSuccess(resp.cache);
} catch (e) {
if (e is SdkException) {
return ListCachesError(e);
} else {
return ListCachesError(
UnknownException("Unexpected error: $e", null, null));
}
}
}
}
10 changes: 4 additions & 6 deletions lib/src/internal/data_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,17 @@ abstract class AbstractDataClient {
class DataClient implements AbstractDataClient {
late ClientChannel _channel;
late ScsClient _client;
late CacheConfiguration _configuration;
late Duration _defaultTtl;
final CacheConfiguration _configuration;
final Duration _defaultTtl;

DataClient(CredentialProvider credentialProvider,
CacheConfiguration configuration, Duration defaultTtl) {
DataClient(CredentialProvider credentialProvider, this._configuration,
this._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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:client_sdk_dart/src/messages/responses/responses_base.dart';

/// Sealed class for a create cache response.
///
/// Pattern matching can be used to operate on the appropriate subtype.
/// ```
/// switch (response) {
/// case CreateCacheSuccess():
/// // handle success
/// case AlreadyExists():
/// // handle already exists
/// case CreateCacheError():
/// // handle error
/// }
/// ```
sealed class CreateCacheResponse {}

/// Indicates a successful create cache request.
class CreateCacheSuccess implements CreateCacheResponse {}

/// Indicates that the cache already exists, so there was nothing to do.
class AlreadyExists implements CreateCacheResponse {}

/// Indicates that an error occurred during the create cache 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 CreateCacheError extends ErrorResponseBase
implements CreateCacheResponse {
CreateCacheError(super.exception);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:client_sdk_dart/src/messages/responses/responses_base.dart';

/// Sealed class for a delete cache response.
///
/// Pattern matching can be used to operate on the appropriate subtype.
/// ```
/// switch (response) {
/// case DeleteCacheSuccess():
/// // handle success
/// case DeleteCacheError():
/// // handle error
/// }
/// ```
sealed class DeleteCacheResponse {}

/// Indicates a successful delete cache request.
class DeleteCacheSuccess implements DeleteCacheResponse {}

/// Indicates that an error occurred during the delete cache 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 DeleteCacheError extends ErrorResponseBase
implements DeleteCacheResponse {
DeleteCacheError(super.exception);
}
46 changes: 46 additions & 0 deletions lib/src/messages/responses/cache/control/list_caches_response.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:client_sdk_dart/generated/controlclient.pb.dart';
import 'package:client_sdk_dart/src/messages/responses/responses_base.dart';

/// Represents information about a listed cache, such as its name.
/// May include additional information in the future.
class CacheInfo {
final String name;

CacheInfo(this.name);
}

/// Sealed class for a list caches response.
///
/// Pattern matching can be used to operate on the appropriate subtype.
/// ```
/// switch (response) {
/// case ListCachesSuccess():
/// // handle success
/// case ListCachesError():
/// // handle error
/// }
/// ```
sealed class ListCachesResponse {}

/// Indicates a successful list caches request.
class ListCachesSuccess implements ListCachesResponse {
late final List<CacheInfo> caches;

ListCachesSuccess(List<Cache_> grpcCaches) {
caches = grpcCaches.map((cache) => CacheInfo(cache.cacheName)).toList();
}

String description() {
return "[ListCachesSuccess] length of caches list: ${caches.length}";
}
}

/// Indicates that an error occurred during the list caches 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 ListCachesError extends ErrorResponseBase implements ListCachesResponse {
ListCachesError(super.exception);
}
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies:
logging: ^1.2.0
protobuf: ^3.1.0
string_validator: ^1.0.2
uuid: ^4.3.1
# path: ^1.8.0

dev_dependencies:
Expand Down
Loading