Skip to content

Commit

Permalink
Merge pull request #20 from splitio/task/flagSets
Browse files Browse the repository at this point in the history
FlagSets support
  • Loading branch information
mmelograno authored Jan 25, 2024
2 parents 78681b8 + 454ff82 commit 5332009
Show file tree
Hide file tree
Showing 35 changed files with 1,430 additions and 32 deletions.
10 changes: 10 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
version: 2

updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
reviewers:
- "splitio/sdk"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ vendor
.phpunit.cache
coverage.xml
coverage.html/
*.local.php
8 changes: 8 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
1.5.0 (Jan 25, 2024)
- Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation):
- Added new variations of the get treatment methods to support evaluating flags in given flag set/s.
- getTreatmentsByFlagSet and getTreatmentsByFlagSets
- getTreatmentWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets
- Updated the following SDK manager method to expose flag sets on flag views:
- Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager.

1.4.0 (Dec 14, 2023):
- Add support for a custom tracer for client methods.
- Support finer granularity on timeouts.
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions examples/treatmentsByFlagSet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

require_once '../vendor/autoload.php';

use \SplitIO\ThinSdk\Factory;
use \SplitIO\ThinSdk\Utils\ImpressionListener;
use \SplitIO\ThinSdk\Models\Impression;

class CustomListener implements ImpressionListener
{
public function accept(Impression $i, ?array $a)
{
echo "got an impression for: key=".$i->getKey()
." feat=".$i->getFeature()
." treatment=".$i->getTreatment()
." label=".$i->getLabel()
." cn=".$i->getChangeNumber()
." #attrs=".(($a == null) ? 0 : count($a))."\n";
}
}

$factory = Factory::withConfig([
'transfer' => [
'address' => '../../splitd/splitd.sock',
'type' => 'unix-stream',
],
'logging' => [
'level' => \Psr\Log\LogLevel::INFO,
],
'utils' => [
'impressionListener' => new CustomListener(),
],
]);

$client = $factory->client();
print_r($client->getTreatmentsByFlagSet("key", null, "server_side", ['age' => 22]));
print_r($client->getTreatmentsWithConfigByFlagSet("key", null, "server_side", ['age' => 22]));
37 changes: 37 additions & 0 deletions examples/treatmentsByFlagSets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

require_once '../vendor/autoload.php';

use \SplitIO\ThinSdk\Factory;
use \SplitIO\ThinSdk\Utils\ImpressionListener;
use \SplitIO\ThinSdk\Models\Impression;

class CustomListener implements ImpressionListener
{
public function accept(Impression $i, ?array $a)
{
echo "got an impression for: key=".$i->getKey()
." feat=".$i->getFeature()
." treatment=".$i->getTreatment()
." label=".$i->getLabel()
." cn=".$i->getChangeNumber()
." #attrs=".(($a == null) ? 0 : count($a))."\n";
}
}

$factory = Factory::withConfig([
'transfer' => [
'address' => '../../splitd/splitd.sock',
'type' => 'unix-stream',
],
'logging' => [
'level' => \Psr\Log\LogLevel::INFO,
],
'utils' => [
'impressionListener' => new CustomListener(),
],
]);

$client = $factory->client();
print_r($client->getTreatmentsByFlagSets("key", null, ["server_side", "backend"], ['age' => 22]));
print_r($client->getTreatmentsWithConfigByFlagSets("key", null, ["server_side", "backend"], ['age' => 22]));
177 changes: 176 additions & 1 deletion src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, arra
$this->tracer->trace(TEF::forStart($method, $id, $this->tracer->includeArgs() ? func_get_args() : []));
$toReturn = $this->cache->getManyWithConfig($key, $features, $attributes);
$features = self::getMissing($toReturn);

if (count($features) == 0) {
return $toReturn;
}
Expand All @@ -156,6 +155,182 @@ public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, arra
}
}

public function getTreatmentsByFlagSet(
string $key,
?string $bucketingKey,
string $flagSet,
?array $attributes
): array {
try {
$id = $this->tracer->makeId();
$method = Tracer::METHOD_GET_TREATMENTS_BY_FLAG_SET;
$this->tracer->trace(TEF::forStart($method, $id, $this->tracer->includeArgs() ? func_get_args() : []));
$flagSet = $this->inputValidator->sanitize($flagSet, 'getTreatmentsByFlagSet');
if (is_null($flagSet)) {
return array();
}
$featuresFromSet = $this->cache->getFeaturesByFlagSets([$flagSet]);
if (!is_null($featuresFromSet)) {
$toReturn = $this->cache->getMany($key, $featuresFromSet, $attributes);
$features = self::getMissing($toReturn);
if (count($features) == 0) {
return $toReturn;
}
}

$this->tracer->trace(TEF::forRPCStart($method, $id));
$results = $this->lm->getTreatmentsByFlagSet($key, $bucketingKey, $flagSet, $attributes);
$this->tracer->trace(TEF::forRPCEnd($method, $id));
$toReturn = array();
foreach ($results as $feature => $result) {
list($treatment, $ilData) = $result;
$toReturn[$feature] = $treatment;
$this->handleListener($key, $bucketingKey, $feature, $attributes, $treatment, $ilData);
}
$this->cache->setFeaturesForFlagSets([$flagSet], array_keys($results));
$this->cache->setMany($key, $attributes, $toReturn);
return $toReturn;
} catch (\Exception $exc) {
$this->tracer->trace(TEF::forException($method, $id, $exc));
$this->logger->error($exc);
return array();
} finally {
$this->tracer->trace(TEF::forEnd($method, $id));
}
}

public function getTreatmentsWithConfigByFlagSet(
string $key,
?string $bucketingKey,
string $flagSet,
?array $attributes = null
): array {
try {
$id = $this->tracer->makeId();
$method = Tracer::METHOD_GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET;
$this->tracer->trace(TEF::forStart($method, $id, $this->tracer->includeArgs() ? func_get_args() : []));
$flagSet = $this->inputValidator->sanitize($flagSet, 'getTreatmentsWithConfigByFlagSet');
if (is_null($flagSet)) {
return array();
}
$featuresFromSet = $this->cache->getFeaturesByFlagSets([$flagSet]);
if (!is_null($featuresFromSet)) {
$toReturn = $this->cache->getManyWithConfig($key, $featuresFromSet, $attributes);
$features = self::getMissing($toReturn);
if (count($features) == 0) {
return $toReturn;
}
}

$this->tracer->trace(TEF::forRPCStart($method, $id));
$results = $this->lm->getTreatmentsWithConfigByFlagSet($key, $bucketingKey, $flagSet, $attributes);
$this->tracer->trace(TEF::forRPCEnd($method, $id));
$toReturn = array();
foreach ($results as $feature => $result) {
list($treatment, $ilData, $config) = $result;
$toReturn[$feature] = ['treatment' => $treatment, 'config' => $config];
$this->handleListener($key, $bucketingKey, $feature, $attributes, $treatment, $ilData);
}
$this->cache->setFeaturesForFlagSets([$flagSet], array_keys($results));
$this->cache->setManyWithConfig($key, $attributes, $toReturn);
return $toReturn;
} catch (\Exception $exc) {
$this->tracer->trace(TEF::forException($method, $id, $exc));
$this->logger->error($exc);
return array();
} finally {
$this->tracer->trace(TEF::forEnd($method, $id));
}
}

public function getTreatmentsByFlagSets(
string $key,
?string $bucketingKey,
array $flagSets,
?array $attributes
): array {
try {
$id = $this->tracer->makeId();
$method = Tracer::METHOD_GET_TREATMENTS_BY_FLAG_SETS;
$this->tracer->trace(TEF::forStart($method, $id, $this->tracer->includeArgs() ? func_get_args() : []));
$flagSets = $this->inputValidator->sanitizeMany($flagSets, 'getTreatmentsByFlagSets');
if (is_null($flagSets)) {
return array();
}
$featuresFromSets = $this->cache->getFeaturesByFlagSets($flagSets);
if (!is_null($featuresFromSets)) {
$toReturn = $this->cache->getMany($key, $featuresFromSets, $attributes);
$features = self::getMissing($toReturn);
if (count($features) == 0) {
return $toReturn;
}
}

$this->tracer->trace(TEF::forRPCStart($method, $id));
$results = $this->lm->getTreatmentsByFlagSets($key, $bucketingKey, $flagSets, $attributes);
$this->tracer->trace(TEF::forRPCEnd($method, $id));
$toReturn = array();
foreach ($results as $feature => $result) {
list($treatment, $ilData) = $result;
$toReturn[$feature] = $treatment;
$this->handleListener($key, $bucketingKey, $feature, $attributes, $treatment, $ilData);
}
$this->cache->setFeaturesForFlagSets($flagSets, array_keys($results));
$this->cache->setMany($key, $attributes, $toReturn);
return $toReturn;
} catch (\Exception $exc) {
$this->tracer->trace(TEF::forException($method, $id, $exc));
$this->logger->error($exc);
return array();
} finally {
$this->tracer->trace(TEF::forEnd($method, $id));
}
}

public function getTreatmentsWithConfigByFlagSets(
string $key,
?string $bucketingKey,
array $flagSets,
?array $attributes = null
): array {
try {
$id = $this->tracer->makeId();
$method = Tracer::METHOD_GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS;
$this->tracer->trace(TEF::forStart($method, $id, $this->tracer->includeArgs() ? func_get_args() : []));
$flagSets = $this->inputValidator->sanitizeMany($flagSets, 'getTreatmentsWithConfigByFlagSets');
if (is_null($flagSets)) {
return array();
}
$featuresFromSet = $this->cache->getFeaturesByFlagSets($flagSets);
if (!is_null($featuresFromSet)) {
$toReturn = $this->cache->getManyWithConfig($key, $featuresFromSet, $attributes);
$features = self::getMissing($toReturn);
if (count($features) == 0) {
return $toReturn;
}
}

$this->tracer->trace(TEF::forRPCStart($method, $id));
$results = $this->lm->getTreatmentsWithConfigByFlagSets($key, $bucketingKey, $flagSets, $attributes);
$this->tracer->trace(TEF::forRPCEnd($method, $id));
$toReturn = array();
foreach ($results as $feature => $result) {
list($treatment, $ilData, $config) = $result;
$toReturn[$feature] = ['treatment' => $treatment, 'config' => $config];
$this->handleListener($key, $bucketingKey, $feature, $attributes, $treatment, $ilData);
}
$this->cache->setFeaturesForFlagSets($flagSets, array_keys($results));
$this->cache->setManyWithConfig($key, $attributes, $toReturn);
return $toReturn;
} catch (\Exception $exc) {
$this->tracer->trace(TEF::forException($method, $id, $exc));
$this->logger->error($exc);
return array();
} finally {
$this->tracer->trace(TEF::forEnd($method, $id));
}
}

public function track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool
{
try {
Expand Down
14 changes: 14 additions & 0 deletions src/ClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,19 @@ function getTreatment(string $key, ?string $bucketingKey, string $feature, ?arra
function getTreatmentWithConfig(string $key, ?string $bucketingKey, string $feature, ?array $attributes): array;
function getTreatments(string $key, ?string $bucketingKey, array $features, ?array $attributes): array;
function getTreatmentsWithConfig(string $key, ?string $bucketingKey, array $features, ?array $attributes): array;
function getTreatmentsByFlagSet(string $key, ?string $bucketingKey, string $flagSet, ?array $attributes): array;
function getTreatmentsWithConfigByFlagSet(
string $key,
?string $bucketingKey,
string $flagSet,
?array $attributes
): array;
function getTreatmentsByFlagSets(string $key, ?string $bucketingKey, array $flagSets, ?array $attributes): array;
function getTreatmentsWithConfigByFlagSets(
string $key,
?string $bucketingKey,
array $flagSets,
?array $attributes
): array;
function track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool;
}
24 changes: 22 additions & 2 deletions src/Fallback/AlwaysControlClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,42 @@ public function getTreatmentWithConfig(string $key, ?string $bucketingKey, strin
return ['treatment' => 'control', 'config' => null];
}

public function getTreatments(string $key, ?string $bucketingKey, array $features, ?array $attributes): array
public function getTreatments(string $key, ?string $bucketingKey, array $features, ?array $attributes): array
{
return array_reduce($features, function ($carry, $item) {
$carry[$item] = "control";
return $carry;
}, []);
}

public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, array $features, ?array $attributes): array
public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, array $features, ?array $attributes): array
{
return array_reduce($features, function ($carry, $item) {
$carry[$item] = ['treatment' => 'control', 'config' => null];
return $carry;
}, []);
}

public function getTreatmentsByFlagSet(string $key, $bucketingKey, string $flagSet, $attributes): array
{
return array();
}

public function getTreatmentsWithConfigByFlagSet(string $key, $bucketingKey, string $flagSet, $attributes): array
{
return array();
}

public function getTreatmentsByFlagSets(string $key, $bucketingKey, array $flagSets, $attributes): array
{
return array();
}

public function getTreatmentsWithConfigByFlagSets(string $key, $bucketingKey, array $flagSets, $attributes): array
{
return array();
}

public function track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool
{
return false;
Expand Down
14 changes: 14 additions & 0 deletions src/Link/Consumer/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ function getTreatment(string $key, ?string $bucketingKey, string $feature, ?arra
function getTreatmentWithConfig(string $key, ?string $bucketingKey, string $feature, ?array $attributes): array;
function getTreatments(string $key, ?string $bucketingKey, array $features, ?array $attributes): array;
function getTreatmentsWithConfig(string $key, ?string $bucketingKey, array $features, ?array $attributes): array;
function getTreatmentsByFlagSet(string $key, ?string $bucketingKey, string $flagSet, ?array $attributes): array;
function getTreatmentsWithConfigByFlagSet(
string $key,
?string $bucketingKey,
string $flagSet,
?array $attributes
): array;
function getTreatmentsByFlagSets(string $key, ?string $bucketingKey, array $flagSets, ?array $attributes): array;
function getTreatmentsWithConfigByFlagSets(
string $key,
?string $bucketingKey,
array $flagSet,
?array $attributes
): array;
function track(string $key, string $trafficType, string $eventType, ?float $value, ?array $properties): bool;
function splitNames(): array;
function split(string $splitName): ?SplitView;
Expand Down
Loading

0 comments on commit 5332009

Please sign in to comment.