Skip to content

Commit

Permalink
feat: cache client control plane operations (#44)
Browse files Browse the repository at this point in the history
* feat: cache client control plane operations

* pass auth token to ci

* remove unnecessary commented out tests

* empty commit to nudge ci

* shorten response name to AlreadyExists
  • Loading branch information
anitarua authored Jan 2, 2024
1 parent 50e54a9 commit a71ed4d
Show file tree
Hide file tree
Showing 14 changed files with 356 additions and 18 deletions.
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());

// 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);
_logger.finest("initializing cache client");
}

@override
Future<CreateCacheResponse> createCache(String cacheName) {
// TODO: add validators
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

0 comments on commit a71ed4d

Please sign in to comment.