Skip to content

Commit

Permalink
fix: added clent for experimentation system
Browse files Browse the repository at this point in the history
  • Loading branch information
Subhash703 committed Jul 29, 2024
1 parent 0fa5e0b commit 1b95516
Show file tree
Hide file tree
Showing 12 changed files with 584 additions and 2 deletions.
2 changes: 0 additions & 2 deletions clients/dart/dart_cac_client/test/dart_cac_client_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:isolate';

import 'package:flutter_test/flutter_test.dart';

import 'package:dart_cac_client/dart_cac_client.dart';
Expand Down
29 changes: 29 additions & 0 deletions clients/dart/dart_exp_client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/
10 changes: 10 additions & 0 deletions clients/dart/dart_exp_client/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "ba393198430278b6595976de84fe170f553cc728"
channel: "stable"

project_type: package
3 changes: 3 additions & 0 deletions clients/dart/dart_exp_client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 0.0.1

* TODO: Describe initial release.
1 change: 1 addition & 0 deletions clients/dart/dart_exp_client/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TODO: Add your license here.
68 changes: 68 additions & 0 deletions clients/dart/dart_exp_client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Dart Experimentation Client

This package provides a Dart interface for the Experimentation client, allowing you to interact with the experimentation server to retrieve and manage experiments.

## Table of Contents
- [Dart Experimentation Client](#dart-experimentation-client)
- [Table of Contents](#table-of-contents)
- [Usage](#usage)
- [Creating a Client](#creating-a-client)
- [Starting Polling for Updates](#starting-polling-for-updates)
- [Retrieving Configurations](#retrieving-configurations)
- [Getting Default Configurations](#getting-default-configurations)
- [Getting Resolved Configurations](#getting-resolved-configurations)
- [Getting Last Modified Timestamp](#getting-last-modified-timestamp)
- [Disposing the Client](#disposing-the-client)

## Usage

### Creating a Client
To create a new DartExptClient client, instantiate the DartCacClient class with the tenant name, update frequency (in seconds), and host URL

```dart
final client = To create a new DartExptClient client, instantiate the DartCacClient class with the tenant name, update frequency (in seconds), and host URL
('dev', 60, 'http://localhost:8080');
```

### Starting Polling for Updates
Use the exptStartPolling method to start polling for configuration updates for the specified tenant:

```dart
client.exptStartPolling("<tenant name>");
```

### Retrieving Configurations
Use the getConfigs method to retrieve configurations based on a filter query and filter prefix:
```dart
String configs = client.getConfigs('{"country": "India"}', 'country');
```

### Getting Default Configurations
Use the getDefaultConfig method to retrieve the default configurations for the specified keys:

```dart
String defaultConfigs = client.getDefaultConfig("india");
```

### Getting Resolved Configurations
Use the getResolvedConfig method to retrieve resolved configurations based on the provided query, keys, and merge strategy:


```dart
String resolvedConfigs = client.getResolvedConfig(
'{"query": "example"}', "key1, key2", MergeStrategy.MERGE/MergeStrategy.REPLACE);
```


### Getting Last Modified Timestamp
Use the getCacLastModified method to get the last modified timestamp of the configurations:

```dart
String lastModified = client.getCacLastModified();
```

### Disposing the Client
Ensure that you dispose of the client properly to free up resources:
```dart
client.dispose();
```
4 changes: 4 additions & 0 deletions clients/dart/dart_exp_client/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
223 changes: 223 additions & 0 deletions clients/dart/dart_exp_client/lib/client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import 'dart:ffi' as ffi;
import 'package:dart_cac_client/types/types.dart';
import 'package:ffi/ffi.dart';
import 'dart:io' show Platform;
import 'package:path/path.dart' as path;

// Define the library
final ffi.DynamicLibrary _lib = ffi.DynamicLibrary.open(_libName);

// Helper to get the correct library name based on the platform
String get _libName {
if (Platform.isWindows) {
return path.join(
'/Users/subhash/working-repos/github/superposition/target/debug/',
'libexperimentation_client.dll');
}
if (Platform.isMacOS) {
return path.join(
'/Users/subhash/working-repos/github/superposition/target/debug/',
'libexperimentation_client.dylib');
}
return path.join(
'/Users/subhash/working-repos/github/superposition/target/debug/',
'libexperimentation_client.so');
}

// Bind the C functions
final ExptNewClientDart _exptNewClient = _lib
.lookup<ffi.NativeFunction<ExptNewClientNative>>('expt_new_client')
.asFunction();

final ExptGetClientDart _exptGetClient = _lib
.lookup<ffi.NativeFunction<ExptGetClientNative>>('expt_get_client')
.asFunction();

final ExptLastErrorLengthDart _exptLastErrorLength = _lib
.lookup<ffi.NativeFunction<ExptLastErrorLengthNative>>(
'expt_last_error_length')
.asFunction();

final ExptLastErrorMessageDart _exptLastErrorMessage = _lib
.lookup<ffi.NativeFunction<ExptLastErrorMessageNative>>(
'expt_last_error_message')
.asFunction();

final ExptFreeStringDart _exptFreeString = _lib
.lookup<ffi.NativeFunction<ExptFreeStringNative>>('expt_free_string')
.asFunction();

final ExpStartPollingUpdateDart _exptStartPollingUpdate = _lib
.lookup<ffi.NativeFunction<ExpStartPollingUpdateNative>>(
'expt_start_polling_update')
.asFunction();

final ExptFreeClientDart _exptFreeClient = _lib
.lookup<ffi.NativeFunction<ExptFreeClientNative>>('expt_free_client')
.asFunction();

final ExptGetApplicableVariantDart _exptGetApplicableVariant = _lib
.lookup<ffi.NativeFunction<ExptGetApplicableVariantNative>>(
'expt_get_applicable_variant')
.asFunction();

final ExptGetSatisfiedExperimentsDart _exptGetSatisfiedExperiments = _lib
.lookup<ffi.NativeFunction<ExptGetSatisfiedExperimentsNative>>(
'expt_get_satisfied_experiments')
.asFunction();

final ExptGetFilteredSatisfiedExperimentsDart
_exptGetFilteredSatisfiedExperiments = _lib
.lookup<ffi.NativeFunction<ExptGetFilteredSatisfiedExperimentsNative>>(
'expt_get_filtered_satisfied_experiments')
.asFunction();

final ExptGetRunningExperimentsDart _exptGetRunningExperiments = _lib
.lookup<ffi.NativeFunction<ExptGetRunningExperimentsNative>>(
'expt_get_running_experiments')
.asFunction();

// Dart wrapper class
class ExptClient {
late ffi.Pointer<ffi.Void> _clientPtr;

ExptClient(String tenant, int updateFrequency, String hostname) {
final tenantPtr = tenant.toNativeUtf8();
final hostnamePtr = hostname.toNativeUtf8();

final result = _exptNewClient(tenantPtr, updateFrequency, hostnamePtr);
print("Expt Client result: $result");

_exptFreeString(tenantPtr);
_exptFreeString(hostnamePtr);

if (result != 0) {
final errorPtr = _exptLastErrorMessage();
final errorMessage = errorPtr.toDartString();
print("Error message: $errorMessage");
_exptFreeString(errorPtr);
throw Exception("Failed to create Experimentation client: $errorMessage");
}

_clientPtr = _exptGetClient(tenant.toNativeUtf8());
if (_clientPtr == ffi.nullptr) {
final errorPtr = _exptLastErrorMessage();
final errorMessage = errorPtr.toDartString();
print("Error getting client pointer: $errorMessage");
_exptFreeString(errorPtr);
throw Exception("Failed to get Experimentation client: $errorMessage");
}
print("Client pointer obtained successfully");
}

String getApplicableVariants(String context, int toss) {
final clientPtr = context.toNativeUtf8();
final tossPtr = malloc.allocate<ffi.Int16>(ffi.sizeOf<ffi.Int16>());
tossPtr.value = toss;
final applicableVariantPtr =
_exptGetApplicableVariant(_clientPtr, clientPtr, tossPtr);

_exptFreeString(clientPtr);
malloc.free(tossPtr);

if (applicableVariantPtr == ffi.nullptr) {
final errorPtr = _exptLastErrorMessage();
final errorMessage = errorPtr.toDartString();
print("Error getting Applicable Variant: $errorMessage");
_exptFreeString(errorPtr);
throw Exception("Failed to get config: $errorMessage");
}
final applicableVariant = applicableVariantPtr.toDartString();
print("Received Applicable Variant: $applicableVariant");
_exptFreeString(applicableVariantPtr);

return applicableVariant;
}

String getSatisfiedExperiments(String context, String filterPrefix) {
final contextPtr = context.toNativeUtf8();
final filterPrefixPtr = filterPrefix.toNativeUtf8();

final satisfiedExperimentsPtr =
_exptGetSatisfiedExperiments(_clientPtr, contextPtr, filterPrefixPtr);

_exptFreeString(contextPtr);
_exptFreeString(filterPrefixPtr);

if (satisfiedExperimentsPtr == ffi.nullptr) {
final errorPtr = _exptLastErrorMessage();
final errorMessage = errorPtr.toDartString();
print("Error getting satisfied experiments: $errorMessage");
_exptFreeString(errorPtr);
throw Exception("Failed to get satisfied experiments: $errorMessage");
}

final satisfiedExperiments = satisfiedExperimentsPtr.toDartString();
print("Received satisfied experiments: $satisfiedExperiments");
_exptFreeString(satisfiedExperimentsPtr);

return satisfiedExperiments;
}

String getFilteredSatisfiedExperiments(String context, String filterPrefix) {
if (_clientPtr == ffi.nullptr) {
throw Exception("Failed to get Experimentation client!");
}
final contextPtr = context.toNativeUtf8();
final filterPrefixPtr = filterPrefix.toNativeUtf8();

final fltSatisfiedExperimentsPtr = _exptGetFilteredSatisfiedExperiments(
_clientPtr, contextPtr, filterPrefixPtr);

_exptFreeString(contextPtr);
_exptFreeString(filterPrefixPtr);

if (fltSatisfiedExperimentsPtr == ffi.nullptr) {
final errorPtr = _exptLastErrorMessage();
final errorMessage = errorPtr.toDartString();
print("Error getting filtered satisfied experiments : $errorMessage");
_exptFreeString(errorPtr);
throw Exception(
"Failed to get filtered satisfied experiments: $errorMessage");
}
var fltSatisfiedExperiments = fltSatisfiedExperimentsPtr.toDartString();
_exptFreeString(fltSatisfiedExperimentsPtr);

return fltSatisfiedExperiments;
}

String getRunningExperiments() {
if (_clientPtr == ffi.nullptr) {
throw Exception("Failed to get Experimentation client!");
}
final runningExprsPtr = _exptGetRunningExperiments(_clientPtr);
if (runningExprsPtr == ffi.nullptr) {
final errorPtr = _exptLastErrorMessage();
final errorMessage = errorPtr.toDartString();
print("Error getting filtered running experiments : $errorMessage");
_exptFreeString(errorPtr);
throw Exception(
"Failed to get filtered running experiments: $errorMessage");
}
var runningExprs = runningExprsPtr.toDartString();
_exptFreeString(runningExprsPtr);
return runningExprs;
}

void startPollingUpdate(String tenant) {
if (_clientPtr == ffi.nullptr) {
throw Exception("Failed to get Experimentation client!");
}
final tenantPtr = tenant.toNativeUtf8();
_exptStartPollingUpdate(tenantPtr);
_exptFreeString(tenantPtr);
print("finish polling");
}

void dispose() {
if (_clientPtr != ffi.nullptr) {
_exptFreeClient(_clientPtr);
_clientPtr = ffi.nullptr;
}
}
}
Loading

0 comments on commit 1b95516

Please sign in to comment.