Skip to content

Commit

Permalink
added support to treatmentsByFlagSet
Browse files Browse the repository at this point in the history
  • Loading branch information
mmelograno committed Dec 21, 2023
1 parent e2db61a commit 95da73f
Show file tree
Hide file tree
Showing 13 changed files with 318 additions and 0 deletions.
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
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]));
38 changes: 38 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,44 @@ public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, arra
}
}

public function getTreatmentsByFlagSet(string $key, ?string $bucketingKey, string $flagSet, ?array $attributes): array
{
try {
// @TODO implement cache for this method

$results = $this->lm->getTreatmentsByFlagSet($key, $bucketingKey, $flagSet, $attributes);
foreach ($results as $feature => $result) {
list($treatment, $ilData) = $result;
$toReturn[$feature] = $treatment;
$this->handleListener($key, $bucketingKey, $feature, $attributes, $treatment, $ilData);
}
$this->cache->setMany($key, $attributes, $toReturn);
return $toReturn;
} catch (\Exception $exc) {
$this->logger->error($exc);
return array();
}
}

public function getTreatmentsWithConfigByFlagSet(string $key, ?string $bucketingKey, string $flagSet, ?array $attributes = null): array
{
try {
// @TODO implement cache for this method

$results = $this->lm->getTreatmentsWithConfigByFlagSet($key, $bucketingKey, $flagSet, $attributes);
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->setMany($key, $attributes, $toReturn);
return $toReturn;
} catch (\Exception $exc) {
$this->logger->error($exc);
return array();
}
}

public function track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool
{
try {
Expand Down
2 changes: 2 additions & 0 deletions src/ClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ 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 track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool;
}
10 changes: 10 additions & 0 deletions src/Fallback/AlwaysControlClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, arr
}, []);
}

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 track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool
{
return false;
Expand Down
2 changes: 2 additions & 0 deletions src/Link/Consumer/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ 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 track(string $key, string $trafficType, string $eventType, ?float $value, ?array $properties): bool;
function splitNames(): array;
function split(string $splitName): ?SplitView;
Expand Down
38 changes: 38 additions & 0 deletions src/Link/Consumer/V1Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,44 @@ public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, arra
return $results;
}

public function getTreatmentsByFlagSet(string $key, ?string $bucketingKey, string $flagSet, ?array $attributes): array
{
$response = Protocol\V1\TreatmentsByFlagSetResponse::fromRaw(
$this->rpcWithReconnect(RPC::forTreatmentsByFlagSet($key, $bucketingKey, $flagSet, $attributes))
);
$response->ensureSuccess();

$results = [];

foreach($response->getEvaluationResults() as $feature=>$evalResult) {
$results[$feature] = $evalResult == null
? ["control", null, null]
: [$evalResult->getTreatment(), $evalResult->getImpressionListenerdata(), $evalResult->getConfig()];
}

return $results;
}


public function getTreatmentsWithConfigByFlagSet(string $key, ?string $bucketingKey, string $flagSet, ?array $attributes): array
{
$response = Protocol\V1\TreatmentsByFlagSetResponse::fromRaw(
$this->rpcWithReconnect(RPC::forTreatmentsWithConfigByFlagSet($key, $bucketingKey, $flagSet, $attributes))
);
$response->ensureSuccess();

$results = [];

foreach($response->getEvaluationResults() as $feature=>$evalResult) {
$results[$feature] = $evalResult == null
? ["control", null, null]
: [$evalResult->getTreatment(), $evalResult->getImpressionListenerdata(), $evalResult->getConfig()];
}

return $results;
}


public function track(string $key, string $trafficType, string $eventType, ?float $value, ?array $properties): bool
{
$response = Protocol\V1\TrackResponse::fromRaw(
Expand Down
2 changes: 2 additions & 0 deletions src/Link/Protocol/V1/OpCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class OpCode extends Enum
private const Treatments = 0x12;
private const TreatmentWithConfig = 0x13;
private const TreatmentsWithConfig = 0x14;
private const TreatmentsByFlagSet = 0x15;
private const TreatmentsWithConfigByFlagSet = 0x16;

private const Track = 0x80;

Expand Down
24 changes: 24 additions & 0 deletions src/Link/Protocol/V1/RPC.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ public static function forTreatmentsWithConfig(string $key, ?string $bucketingKe
return self::_forTreatments($key, $bucketingKey, $features, $attributes, true);
}

public static function forTreatmentsByFlagSet(string $key, ?string $bucketingKey, string $flagSet, ?array $attributes): RPC
{
return self::_forTreatmentsByFlagSet($key, $bucketingKey, $flagSet, $attributes, false);
}

public static function forTreatmentsWithConfigByFlagSet(string $key, ?string $bucketingKey, string $flagSet, ?array $attributes): RPC
{
return self::_forTreatmentsByFlagSet($key, $bucketingKey, $flagSet, $attributes, true);
}

public static function forTrack(
string $key,
string $trafficType,
Expand Down Expand Up @@ -138,4 +148,18 @@ public static function _forTreatments(string $k, ?string $bk, array $f, ?array $
]
);
}

public static function _forTreatmentsByFlagSet(string $k, ?string $bk, string $f, ?array $a, bool $includeConfig): RPC
{
return new RPC(
Version::V1(),
$includeConfig ? OpCode::TreatmentsWithConfigByFlagSet() : OpCode::TreatmentsByFlagSet(),
[
TreatmentsByFlagSetArgs::KEY()->getValue() => $k,
TreatmentsByFlagSetArgs::BUCKETING_KEY()->getValue() => $bk,
TreatmentsByFlagSetArgs::FLAG_SET()->getValue() => $f,
TreatmentsByFlagSetArgs::ATTRIBUTES()->getValue() => ($a != null && count($a) > 0) ? $a : null,
]
);
}
}
25 changes: 25 additions & 0 deletions src/Link/Protocol/V1/TreatmentsByFlagSetArgs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace SplitIO\ThinSdk\Link\Protocol\V1;

/*
enum TreatmentsByFlagSetArgs: int
{
case KEY = 0;
case BUCKETING_KEY = 1;
case FLAGSET = 2;
case ATTRIBUTES = 3;
}
*/

use MyCLabs\Enum\Enum;

class TreatmentsByFlagSetArgs extends Enum
{
private const KEY = 0;
private const BUCKETING_KEY = 1;
private const FLAG_SET = 2;
private const ATTRIBUTES = 3;

}

43 changes: 43 additions & 0 deletions src/Link/Protocol/V1/TreatmentsByFlagSetResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace SplitIO\ThinSdk\Link\Protocol\V1;

use SplitIO\ThinSdk\Link\Protocol\V1\Result;
use SplitIO\ThinSdk\Foundation\Lang\Enforce;

class TreatmentsByFlagSetResponse extends Response
{

private /*array*/ $evaluationResults;

public function __construct(Result $status, array $results)
{
parent::__construct($status);
$this->evaluationResults = $results;
}

public function getEvaluationResults(): array
{
return $this->evaluationResults;
}

public function getEvaluationResult(int $index): ?EvaluationResult
{
return count($this->evaluationResults) > $index ? $this->evaluationResults[$index] : null;
}

public static function fromRaw(/*mixed*/$raw)/*: mixed*/
{
Enforce::isArray($raw);

$results = [];
foreach (Enforce::isArray($raw['p']['r']) as $feature => $evalResult) {
$results[$feature] = EvaluationResult::fromRaw(Enforce::isArray($evalResult));
};

return new TreatmentsByFlagSetResponse(
Result::from(Enforce::isInt($raw['s'])),
Enforce::isArray($results)
);
}
}
89 changes: 89 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,81 @@ public function testGetTreatmentsWithConfigAndListener()
);
}

public function testGetTreatmentsByFlagSetNoImpListener() {
$manager = $this->createMock(Manager::class);
$manager->expects($this->once())->method('getTreatmentsByFlagSet')
->with('someKey', 'someBuck', 'someSet', ['someAttr' => 123])
->willReturn([
'someFeature1' => ['on', null, null],
'someFeature2' => ['off', null, null],
'someFeature3' => ['n/a', null, null],
]);

$client = new Client($manager, $this->logger, null);
$this->assertEquals(
['someFeature1' => 'on', 'someFeature2' => 'off', 'someFeature3' => 'n/a'],
$client->getTreatmentsByFlagSet('someKey', 'someBuck', 'someSet', ['someAttr' => 123])
);
}

public function testGetTreatmentsByFlagSetWithImpListener()
{
$manager = $this->createMock(Manager::class);
$manager->expects($this->once())->method('getTreatmentsByFlagSet')
->with('someKey', 'someBuck', 'someSet', ['someAttr' => 123])
->willReturn([
'someFeature1' => ['on', new ImpressionListenerData('lab1', 123, 123456), null],
'someFeature2' => ['off', new ImpressionListenerData('lab1', 124, 123457), null],
'someFeature3' => ['n/a', new ImpressionListenerData('lab1', 125, 123458), null],
]);

$ilMock = $this->createMock(ImpressionListener::class);
$ilMock->expects($this->exactly(3))
->method('accept')
->withConsecutive(
[new Impression('someKey', 'someBuck', 'someFeature1', 'on', 'lab1', 123, 123456), ['someAttr' => 123]],
[new Impression('someKey', 'someBuck', 'someFeature2', 'off', 'lab1', 124, 123457), ['someAttr' => 123]],
[new Impression('someKey', 'someBuck', 'someFeature3', 'n/a', 'lab1', 125, 123458), ['someAttr' => 123]]
);

$client = new Client($manager, $this->logger, $ilMock);
$this->assertEquals(
['someFeature1' => 'on', 'someFeature2' => 'off', 'someFeature3' => 'n/a'],
$client->getTreatmentsByFlagSet('someKey', 'someBuck', 'someSet', ['someAttr' => 123])
);
}

public function testGetTreatmentsWithConfigByFlagSetAndListener()
{
$manager = $this->createMock(Manager::class);
$manager->expects($this->once())->method('getTreatmentsWithConfigByFlagSet')
->with('someKey', 'someBuck', 'someFlagSet', ['someAttr' => 123])
->willReturn([
'someFeature1' => ['on', new ImpressionListenerData('lab1', 123, 123456), null],
'someFeature2' => ['off', new ImpressionListenerData('lab1', 124, 123457), null],
'someFeature3' => ['n/a', new ImpressionListenerData('lab1', 125, 123458), '{"a": 2}'],
]);

$ilMock = $this->createMock(ImpressionListener::class);
$ilMock->expects($this->exactly(3))
->method('accept')
->withConsecutive(
[new Impression('someKey', 'someBuck', 'someFeature1', 'on', 'lab1', 123, 123456), ['someAttr' => 123]],
[new Impression('someKey', 'someBuck', 'someFeature2', 'off', 'lab1', 124, 123457), ['someAttr' => 123]],
[new Impression('someKey', 'someBuck', 'someFeature3', 'n/a', 'lab1', 125, 123458), ['someAttr' => 123]]
);

$client = new Client($manager, $this->logger, $ilMock);
$this->assertEquals(
[
'someFeature1' => ['treatment' => 'on', 'config' => null],
'someFeature2' => ['treatment' => 'off', 'config' => null],
'someFeature3' => ['treatment' => 'n/a', 'config' => '{"a": 2}']
],
$client->getTreatmentsWithConfigByFlagSet('someKey', 'someBuck', 'someFlagSet', ['someAttr' => 123])
);
}

public function testGetTreatmentExceptionReturnsControl()
{
$manager = $this->createMock(Manager::class);
Expand All @@ -171,6 +246,20 @@ public function testGetTreatmentsExceptionReturnsControl()
);
}

public function testGetTreatmentsByFlagSetExceptionReturnsControl()
{
$manager = $this->createMock(Manager::class);
$manager->expects($this->once())->method('getTreatmentsByFlagSet')
->with('someKey', 'someBuck', 'someSet', ['someAttr' => 123])
->will($this->throwException(new \Exception("abc")));

$client = new Client($manager, $this->logger, null);
$this->assertEquals(
[],
$client->getTreatmentsByFlagSet('someKey', 'someBuck', 'someSet', ['someAttr' => 123])
);
}

public function testGetTreatmentListenerErrorReturnsOk()
{
$manager = $this->createMock(Manager::class);
Expand Down
7 changes: 7 additions & 0 deletions tests/Fallback/AwaysControlClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ public function testGetTreatments()
$this->assertEquals([], $c->getTreatments("key", null, [], null));
}

public function testGetTreatmentsByFlagSets()
{
$c = new AlwaysControlClient();
$this->assertEquals([], $c->getTreatmentsByFlagSet("key", null, "someFlagSet", null));
$this->assertEquals([], $c->getTreatmentsWithConfigByFlagSet("key", null, "someFlagSet", null));
}

public function testTrack()
{
$c = new AlwaysControlClient();
Expand Down

0 comments on commit 95da73f

Please sign in to comment.