From 8ef957fb4b66928883335deeeba0e3e33b5c24eb Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Tue, 4 Mar 2025 21:22:15 -0700 Subject: [PATCH 01/16] lint --- src/Bandits/BanditReferenceIndexer.php | 4 ++-- src/PollingOptions.php | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/PollingOptions.php diff --git a/src/Bandits/BanditReferenceIndexer.php b/src/Bandits/BanditReferenceIndexer.php index 00d3f35..64cd96f 100644 --- a/src/Bandits/BanditReferenceIndexer.php +++ b/src/Bandits/BanditReferenceIndexer.php @@ -84,8 +84,8 @@ private function indexBanditFlags(array $banditVariations): void } } - // array_unique preserves array keys so duplicate entries that are dropped leave holes in the list of numeric keys - // `array_values` builds a new array from the values with reset numeric keys. + // array_unique preserves array keys so duplicate entries that are dropped leave holes in the list of numeric + // keys, then `array_values` builds a new array from the values with reset numeric keys. $this->activeBanditKeys = array_values(array_unique($banditKeys)); } diff --git a/src/PollingOptions.php b/src/PollingOptions.php new file mode 100644 index 0000000..36b6e8b --- /dev/null +++ b/src/PollingOptions.php @@ -0,0 +1,9 @@ + Date: Tue, 4 Mar 2025 21:22:54 -0700 Subject: [PATCH 02/16] feat: mock webserver can change file that's served --- tests/WebServer/MockWebServer.php | 57 +++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/tests/WebServer/MockWebServer.php b/tests/WebServer/MockWebServer.php index 1901a59..c9f3724 100644 --- a/tests/WebServer/MockWebServer.php +++ b/tests/WebServer/MockWebServer.php @@ -7,16 +7,20 @@ class MockWebServer { public readonly string $serverAddress; + public mixed $process; /** - * @param resource $process * @param int $port + * @param string $ufcFile + * @throws Exception */ private function __construct( - public readonly mixed $process, - public readonly int $port + public readonly int $port, + string $ufcFile ) { $this->serverAddress = "localhost:$port"; + $this->serveFile($ufcFile); + usleep(500000); } /** @@ -26,26 +30,22 @@ private function __construct( */ public static function start(string $defaultUFCFile = __DIR__ . '/../data/ufc/flags-v1.json'): MockWebServer { - $descriptorSpec = [ - 0 => ["pipe", "r"], // stdin - 1 => ["pipe", "w"], // stdout - 2 => ["pipe", "w"] // stderr - ]; - $port = self::getFreePort(); + return new self($port, $defaultUFCFile); + } - $server = "localhost:$port"; - - $cmd = "UFC=$defaultUFCFile php -S $server " . __DIR__ . '/router.php'; - $process = proc_open($cmd, $descriptorSpec, $pipes); - if (!is_resource($process)) { - throw new Exception('Unable to start PHP built-in web server.'); - } + /** + * @throws Exception + */ + public function setUfcFile(string $ufcFile): MockWebServer + { + $this->stop(); + $this->serveFile($ufcFile); usleep(500000); - return new self($process, $port); + return $this; } - public function stop() + public function stop(): void { if (!is_resource($this->process)) { return; @@ -62,4 +62,25 @@ private static function getFreePort(): ?int return $port; } + + /** + * @throws Exception + */ + private function serveFile(string $ufcFile): void + { + print("serving file\n"); + $descriptorSpec = [ + 0 => ["pipe", "r"], // stdin + 1 => ["pipe", "w"], // stdout + 2 => ["pipe", "w"] // stderr + ]; + + $cmd = "UFC=$ufcFile php -S $this->serverAddress " . __DIR__ . '/router.php'; + print($cmd . "\n"); + + $this->process = proc_open($cmd, $descriptorSpec, $pipes); + if (!is_resource($this->process)) { + throw new Exception('Unable to start PHP built-in web server.'); + } + } } From c0aed4cb386a8848c0c86b840dc85c17b06421ba Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Tue, 4 Mar 2025 21:24:13 -0700 Subject: [PATCH 03/16] feat: compute cache age in milliseconds --- src/Config/ConfigurationLoader.php | 17 +++++++++++------ tests/Config/ConfigurationLoaderTest.php | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Config/ConfigurationLoader.php b/src/Config/ConfigurationLoader.php index eb53800..367922c 100644 --- a/src/Config/ConfigurationLoader.php +++ b/src/Config/ConfigurationLoader.php @@ -27,7 +27,7 @@ class ConfigurationLoader implements IFlags, IBandits public function __construct( private readonly APIRequestWrapper $apiRequestWrapper, private readonly IConfigurationStore $configurationStore, - private readonly int $cacheAgeLimit = 30, + private readonly int $cacheAgeLimitMillis = 30 * 1000, private readonly bool $optimizedBanditLoading = false ) { $this->parser = new UFCParser(); @@ -65,8 +65,8 @@ public function getBanditByVariation(string $flagKey, string $variation): ?strin */ public function reloadConfigurationIfExpired(): void { - $flagCacheAge = $this->getCacheAgeSeconds(); - if ($flagCacheAge === -1 || $flagCacheAge >= $this->cacheAgeLimit) { + $flagCacheAge = $this->getCacheAgeInMillis(); + if ($flagCacheAge < 0 || $flagCacheAge >= ($this->cacheAgeLimitMillis)) { $this->reloadConfiguration(); } } @@ -112,15 +112,15 @@ function ($json) { } // Store metadata for next time. - $this->configurationStore->setMetadata(self::KEY_FLAG_TIMESTAMP, time()); + $this->configurationStore->setMetadata(self::KEY_FLAG_TIMESTAMP, $this->millitime()); $this->configurationStore->setMetadata(self::KEY_FLAG_ETAG, $response->ETag); } - private function getCacheAgeSeconds(): int + private function getCacheAgeInMillis(): int { $timestamp = $this->configurationStore->getMetadata(self::KEY_FLAG_TIMESTAMP); if ($timestamp != null) { - return time() - $timestamp; + return $this->millitime() - $timestamp; } return -1; } @@ -190,4 +190,9 @@ public function reloadConfiguration(): void $flagETag = $this->configurationStore->getMetadata(self::KEY_FLAG_ETAG); $this->fetchAndStoreConfigurations($flagETag); } + + private function millitime(): int + { + return intval(microtime(true) * 1000); + } } diff --git a/tests/Config/ConfigurationLoaderTest.php b/tests/Config/ConfigurationLoaderTest.php index d140e94..b478f08 100644 --- a/tests/Config/ConfigurationLoaderTest.php +++ b/tests/Config/ConfigurationLoaderTest.php @@ -254,7 +254,7 @@ public function testReloadsOnExpiredCache(): void $cache = DefaultCacheFactory::create(); // Act: Create a new FCL with a 0sec ttl and retrieve a flag - $loader = new ConfigurationLoader($apiWrapper, new ConfigurationStore($cache), cacheAgeLimit: 0); + $loader = new ConfigurationLoader($apiWrapper, new ConfigurationStore($cache), cacheAgeLimitMillis: 0); // Mocks verify interaction of loader <--> API requests and loader <--> config store $apiWrapper->expects($this->exactly(2)) From 4f318f21fd9fa2bc16398dc4761c4b852ad00db7 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Tue, 4 Mar 2025 21:24:39 -0700 Subject: [PATCH 04/16] ignore vscode --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5b3a42f..87b5b01 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ vendor tests/data .phpunit.result.cache +.vscode From 14cc3c62b68e1826267052ecfac66bc4ca0ba34b Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Tue, 4 Mar 2025 21:25:10 -0700 Subject: [PATCH 05/16] feat: Add Polling Options to the EppoClient init method --- src/EppoClient.php | 32 ++++++++++++++++++++++++++------ src/PollingOptions.php | 20 +++++++++++++++++++- tests/EppoClientTest.php | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/EppoClient.php b/src/EppoClient.php index c058c12..fbde83b 100644 --- a/src/EppoClient.php +++ b/src/EppoClient.php @@ -36,13 +36,15 @@ class EppoClient { public const SECOND_MILLIS = 1000; public const MINUTE_MILLIS = 60 * self::SECOND_MILLIS; - public const POLL_INTERVAL_MILLIS = 5 * self::MINUTE_MILLIS; - public const JITTER_MILLIS = 30 * self::SECOND_MILLIS; + public const DEFAULT_POLL_INTERVAL_MILLIS = 5 * self::MINUTE_MILLIS; // 5 minutes. + public const DEFAULT_JITTER_MILLIS = 30 * self::SECOND_MILLIS; + public const DEFAULT_CACHE_AGE_LIMIT = 30 * self::SECOND_MILLIS; // 30 seconds. private static ?EppoClient $instance = null; private RuleEvaluator $evaluator; private IBanditEvaluator $banditEvaluator; + private ?PollingOptions $pollingOptions = null; /** * @param ConfigurationLoader $configurationLoader @@ -82,7 +84,8 @@ public static function init( CacheInterface $cache = null, ClientInterface $httpClient = null, RequestFactoryInterface $requestFactory = null, - ?bool $isGracefulMode = true + ?bool $isGracefulMode = true, + ?PollingOptions $pollingOptions = null, ): EppoClient { // Get SDK metadata to pass as params in the http client. $sdkData = new SDKData(); @@ -114,10 +117,27 @@ public static function init( $baseUrl ); - $configLoader = new ConfigurationLoader($apiWrapper, $configStore); + $cacheAgeLimit = self::DEFAULT_CACHE_AGE_LIMIT; + $interval = self::DEFAULT_POLL_INTERVAL_MILLIS; + $jitter = self::DEFAULT_JITTER_MILLIS; + + if ($pollingOptions !== null) { + if ($pollingOptions->cacheAgeLimitMillis !== null) { + $cacheAgeLimit = $pollingOptions->cacheAgeLimitMillis; + } + if ($pollingOptions->pollingIntervalMillis !== null) { + $interval = $pollingOptions->pollingIntervalMillis; + } + if ($pollingOptions->pollingJitterMillis !== null) { + $jitter = $pollingOptions->pollingJitterMillis; + } + } + + $configLoader = new ConfigurationLoader($apiWrapper, $configStore, $cacheAgeLimit); + $poller = new Poller( - self::POLL_INTERVAL_MILLIS, - self::JITTER_MILLIS, + $interval, + $jitter, function () use ($configLoader) { $configLoader->reloadConfiguration(); } diff --git a/src/PollingOptions.php b/src/PollingOptions.php index 36b6e8b..f42ea97 100644 --- a/src/PollingOptions.php +++ b/src/PollingOptions.php @@ -2,8 +2,26 @@ namespace Eppo; +/** + * Configuration options for the SDKs polling feature. + */ abstract class PollingOptions { + /** + * @var int|null Age limit for cached configuration (when polling is not used). + */ + public ?int $cacheAgeLimitMillis = null; + + /** + * @var int|null Base interval used for polling for new configuration from the API serer. + * + * To use background polling, you must call + * startPolling + */ public ?int $pollingIntervalMillis; + + /** + * @var int|null The maximum amount of random time to adjust each polling interval by. + */ public ?int $pollingJitterMillis; -} \ No newline at end of file +} diff --git a/tests/EppoClientTest.php b/tests/EppoClientTest.php index 5581cea..99af852 100644 --- a/tests/EppoClientTest.php +++ b/tests/EppoClientTest.php @@ -23,6 +23,8 @@ use PHPUnit\Framework\TestCase; use PsrMock\Psr17\RequestFactory; use Throwable; +use ReflectionClass; +use Eppo\PollingOptions; class EppoClientTest extends TestCase { @@ -295,4 +297,41 @@ private function loadTestCases(): array } return $tests; } + + public function testInitWithPollingOptions(): void + { + $apiKey = 'dummy-api-key'; + + $pollingOptions = new class extends PollingOptions { + public ?int $pollingIntervalMillis = 10000; + public ?int $pollingJitterMillis = 2000; + public ?int $cacheAgeLimitMillis = 5; + }; + + $client = EppoClient::init( + $apiKey, + self::$mockServer->serverAddress, + null, + null, + null, + null, + false, + $pollingOptions + ); + + $this->assertEquals( + 3.1415926, + $client->getNumericAssignment(self::EXPERIMENT_NAME, 'subject-10', [], 0) + ); + + self::$mockServer->setUfcFile(__DIR__ . '/data/ufc/bandit-flags-v1.json'); + + // Wait a little bit for the cache to age out. + usleep(10000); + + $this->assertEquals( + 0, + $client->getNumericAssignment(self::EXPERIMENT_NAME, 'subject-10', [], 0) + ); + } } From ed87eb2349c0e7e51e63f3450cbb20b17f26a930 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Tue, 4 Mar 2025 21:29:19 -0700 Subject: [PATCH 06/16] comments --- src/EppoClient.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/EppoClient.php b/src/EppoClient.php index fbde83b..5046ff4 100644 --- a/src/EppoClient.php +++ b/src/EppoClient.php @@ -44,8 +44,6 @@ class EppoClient private RuleEvaluator $evaluator; private IBanditEvaluator $banditEvaluator; - private ?PollingOptions $pollingOptions = null; - /** * @param ConfigurationLoader $configurationLoader * @param PollerInterface $poller @@ -117,10 +115,12 @@ public static function init( $baseUrl ); + // Polling option defaults $cacheAgeLimit = self::DEFAULT_CACHE_AGE_LIMIT; $interval = self::DEFAULT_POLL_INTERVAL_MILLIS; $jitter = self::DEFAULT_JITTER_MILLIS; + // If polling options were passed, use them. if ($pollingOptions !== null) { if ($pollingOptions->cacheAgeLimitMillis !== null) { $cacheAgeLimit = $pollingOptions->cacheAgeLimitMillis; @@ -145,7 +145,6 @@ function () use ($configLoader) { self::$instance = self::createAndInitClient($configLoader, $poller, $assignmentLogger, $isGracefulMode); - return self::$instance; } From 3cc5cb636d310c69d1e75d5806f89e4536df2643 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Tue, 4 Mar 2025 21:29:46 -0700 Subject: [PATCH 07/16] no debug print --- tests/WebServer/MockWebServer.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/WebServer/MockWebServer.php b/tests/WebServer/MockWebServer.php index c9f3724..60abfa7 100644 --- a/tests/WebServer/MockWebServer.php +++ b/tests/WebServer/MockWebServer.php @@ -68,7 +68,6 @@ private static function getFreePort(): ?int */ private function serveFile(string $ufcFile): void { - print("serving file\n"); $descriptorSpec = [ 0 => ["pipe", "r"], // stdin 1 => ["pipe", "w"], // stdout @@ -76,8 +75,6 @@ private function serveFile(string $ufcFile): void ]; $cmd = "UFC=$ufcFile php -S $this->serverAddress " . __DIR__ . '/router.php'; - print($cmd . "\n"); - $this->process = proc_open($cmd, $descriptorSpec, $pipes); if (!is_resource($this->process)) { throw new Exception('Unable to start PHP built-in web server.'); From d10415ca885be216e771f26afcbf39a9e986635d Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Tue, 4 Mar 2025 23:47:24 -0700 Subject: [PATCH 08/16] better class structure for options --- src/PollingOptions.php | 15 +++++++++++---- tests/EppoClientTest.php | 10 +++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/PollingOptions.php b/src/PollingOptions.php index f42ea97..b288d5a 100644 --- a/src/PollingOptions.php +++ b/src/PollingOptions.php @@ -5,12 +5,12 @@ /** * Configuration options for the SDKs polling feature. */ -abstract class PollingOptions +final class PollingOptions { /** * @var int|null Age limit for cached configuration (when polling is not used). */ - public ?int $cacheAgeLimitMillis = null; + public readonly ?int $cacheAgeLimitMillis; /** * @var int|null Base interval used for polling for new configuration from the API serer. @@ -18,10 +18,17 @@ abstract class PollingOptions * To use background polling, you must call * startPolling */ - public ?int $pollingIntervalMillis; + public readonly ?int $pollingIntervalMillis; /** * @var int|null The maximum amount of random time to adjust each polling interval by. */ - public ?int $pollingJitterMillis; + public readonly ?int $pollingJitterMillis; + + public function __constructor(?int $cacheAgeLimitMillis, ?int $pollingIntervalMillis, ?int $pollingJitterMillis) + { + $this->cacheAgeLimitMillis = $cacheAgeLimitMillis; + $this->pollingIntervalMillis = $pollingIntervalMillis; + $this->pollingJitterMillis = $pollingJitterMillis; + } } diff --git a/tests/EppoClientTest.php b/tests/EppoClientTest.php index 99af852..34b0aee 100644 --- a/tests/EppoClientTest.php +++ b/tests/EppoClientTest.php @@ -302,11 +302,11 @@ public function testInitWithPollingOptions(): void { $apiKey = 'dummy-api-key'; - $pollingOptions = new class extends PollingOptions { - public ?int $pollingIntervalMillis = 10000; - public ?int $pollingJitterMillis = 2000; - public ?int $cacheAgeLimitMillis = 5; - }; + $pollingOptions = new PollingOptions( + pollingIntervalMillis: 10000, + pollingJitterMillis: 2000, + cacheAgeLimitMillis: 5 + ); $client = EppoClient::init( $apiKey, From 8e535c8495cd4a4f4cfa109edd3554d82b50fde2 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Wed, 5 Mar 2025 09:57:28 -0700 Subject: [PATCH 09/16] fix constructor --- src/PollingOptions.php | 2 +- tests/EppoClientTest.php | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/PollingOptions.php b/src/PollingOptions.php index b288d5a..25e8c57 100644 --- a/src/PollingOptions.php +++ b/src/PollingOptions.php @@ -25,7 +25,7 @@ final class PollingOptions */ public readonly ?int $pollingJitterMillis; - public function __constructor(?int $cacheAgeLimitMillis, ?int $pollingIntervalMillis, ?int $pollingJitterMillis) + public function __construct(?int $cacheAgeLimitMillis, ?int $pollingIntervalMillis, ?int $pollingJitterMillis) { $this->cacheAgeLimitMillis = $cacheAgeLimitMillis; $this->pollingIntervalMillis = $pollingIntervalMillis; diff --git a/tests/EppoClientTest.php b/tests/EppoClientTest.php index 34b0aee..d9d618b 100644 --- a/tests/EppoClientTest.php +++ b/tests/EppoClientTest.php @@ -298,14 +298,18 @@ private function loadTestCases(): array return $tests; } + /** + * @throws EppoClientInitializationException + * @throws EppoClientException + */ public function testInitWithPollingOptions(): void { $apiKey = 'dummy-api-key'; $pollingOptions = new PollingOptions( + cacheAgeLimitMillis: 5, pollingIntervalMillis: 10000, - pollingJitterMillis: 2000, - cacheAgeLimitMillis: 5 + pollingJitterMillis: 2000 ); $client = EppoClient::init( From 20a4c09a31e5b9c96cb89857e7cc95b9cbf91cf0 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Wed, 5 Mar 2025 14:05:29 -0700 Subject: [PATCH 10/16] chore: refactor webserver sleep --- tests/WebServer/MockWebServer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/WebServer/MockWebServer.php b/tests/WebServer/MockWebServer.php index 60abfa7..dcc3b8a 100644 --- a/tests/WebServer/MockWebServer.php +++ b/tests/WebServer/MockWebServer.php @@ -20,7 +20,6 @@ private function __construct( ) { $this->serverAddress = "localhost:$port"; $this->serveFile($ufcFile); - usleep(500000); } /** @@ -41,7 +40,6 @@ public function setUfcFile(string $ufcFile): MockWebServer { $this->stop(); $this->serveFile($ufcFile); - usleep(500000); return $this; } @@ -79,5 +77,7 @@ private function serveFile(string $ufcFile): void if (!is_resource($this->process)) { throw new Exception('Unable to start PHP built-in web server.'); } + + usleep(500000); } } From b2a1b0ddb45811f83198261f45685b4c78850251 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Wed, 5 Mar 2025 14:06:10 -0700 Subject: [PATCH 11/16] fix test: change configuration using http client intercept instead of mock server which doesn't work properly on CI --- tests/EppoClientTest.php | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/EppoClientTest.php b/tests/EppoClientTest.php index d9d618b..0c5f4c2 100644 --- a/tests/EppoClientTest.php +++ b/tests/EppoClientTest.php @@ -18,12 +18,14 @@ use Eppo\PollerInterface; use Eppo\Tests\WebServer\MockWebServer; use Exception; +use GuzzleHttp\Psr7\Utils; use Http\Discovery\Psr17Factory; use Http\Discovery\Psr18Client; use PHPUnit\Framework\TestCase; +use Psr\Http\Client\ClientInterface; use PsrMock\Psr17\RequestFactory; +use PsrMock\Psr7\Response; use Throwable; -use ReflectionClass; use Eppo\PollingOptions; class EppoClientTest extends TestCase @@ -307,17 +309,25 @@ public function testInitWithPollingOptions(): void $apiKey = 'dummy-api-key'; $pollingOptions = new PollingOptions( - cacheAgeLimitMillis: 5, + cacheAgeLimitMillis: 50, pollingIntervalMillis: 10000, pollingJitterMillis: 2000 ); + $response = new Response(stream: Utils::streamFor(file_get_contents(__DIR__ . '/data/ufc/flags-v1.json'))); + $secondResponse = new Response(stream: Utils::streamFor(file_get_contents(__DIR__ . '/data/ufc/bandit-flags-v1.json'))); + + $httpClient = $this->createMock(ClientInterface::class); + $httpClient->expects($this->atLeast(2)) + ->method('sendRequest') + ->willReturnOnConsecutiveCalls($response, $secondResponse, $secondResponse); + $client = EppoClient::init( $apiKey, - self::$mockServer->serverAddress, - null, + "fake address", null, null, + $httpClient, null, false, $pollingOptions @@ -327,11 +337,8 @@ public function testInitWithPollingOptions(): void 3.1415926, $client->getNumericAssignment(self::EXPERIMENT_NAME, 'subject-10', [], 0) ); - - self::$mockServer->setUfcFile(__DIR__ . '/data/ufc/bandit-flags-v1.json'); - - // Wait a little bit for the cache to age out. - usleep(10000); + // Wait a little bit for the cache to age out and the mock server to spin up. + usleep(75*1000); $this->assertEquals( 0, From 4748dc4f12f5d044f677a348ec1b5e5aaa11e7d1 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Thu, 27 Mar 2025 09:34:18 -0600 Subject: [PATCH 12/16] cleanup test --- tests/EppoClientTest.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/EppoClientTest.php b/tests/EppoClientTest.php index 0c5f4c2..e9df4b6 100644 --- a/tests/EppoClientTest.php +++ b/tests/EppoClientTest.php @@ -325,12 +325,9 @@ public function testInitWithPollingOptions(): void $client = EppoClient::init( $apiKey, "fake address", - null, - null, - $httpClient, - null, - false, - $pollingOptions + httpClient: $httpClient, + isGracefulMode: false, + pollingOptions: $pollingOptions ); $this->assertEquals( From d13358844915205ca2558d14347bc631f70b3d7a Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Thu, 27 Mar 2025 09:35:05 -0600 Subject: [PATCH 13/16] Add fetch and activate config method --- src/EppoClient.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/EppoClient.php b/src/EppoClient.php index 5046ff4..53e7606 100644 --- a/src/EppoClient.php +++ b/src/EppoClient.php @@ -573,6 +573,20 @@ private function checkExpectedType( ($expectedVariationType == VariationType::JSON)); // JSON type check un-necessary here. } + /** + * @throws EppoClientException + */ + public function fetchAndActivateConfiguration(): void + { + try { + $this->configurationLoader->reloadConfiguration(); + } catch (HttpRequestException|InvalidApiKeyException|InvalidConfigurationException $e) { + if ($this->isGracefulMode) { + error_log('[Eppo SDK] Error getting assignment: ' . $e->getMessage()); + } + throw EppoClientException::from($e); + } + } public function startPolling(): void { From 5c90a4e64c9a6d1a65347c1f2d8aedde0972907b Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Thu, 27 Mar 2025 09:44:00 -0600 Subject: [PATCH 14/16] Pull request template --- .github/pull_request_template.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..c776db8 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,16 @@ +[//]: # (Link to the issue corresponding to this chunk of work) +:tickets: Fixes __issue__ +:scroll: Design Doc: __link if applicable__ + +## Motivation and Context +[//]: # (Why is this change required? What problem does it solve?) + +## Description +[//]: # (Describe your changes in detail) + +## How has this been tested? +[//]: # (Please describe in detail how you tested your changes) + +## How has this been documented? +[//]: # (Please provide a summary of documentation updates made, a link to PR with documentation changes, a ticket +requiring changes, or an explanation of why documentation changes are not required) From 6ab60a93b926dc5d8704dc915493ed286166def8 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Thu, 27 Mar 2025 13:38:55 -0600 Subject: [PATCH 15/16] template tweak --- .github/pull_request_template.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c776db8..8061996 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,9 +8,9 @@ ## Description [//]: # (Describe your changes in detail) +## How has this been documented? +[//]: # (Please describe how you documented the developer impact of your changes; link to PRs or issues or explan why no documentation changes are required) + ## How has this been tested? [//]: # (Please describe in detail how you tested your changes) -## How has this been documented? -[//]: # (Please provide a summary of documentation updates made, a link to PR with documentation changes, a ticket -requiring changes, or an explanation of why documentation changes are not required) From 73f319d13fad01a04da0bde9cd1d134666e3bd14 Mon Sep 17 00:00:00 2001 From: Ty Potter Date: Fri, 28 Mar 2025 09:15:41 -0600 Subject: [PATCH 16/16] fix --- src/EppoClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EppoClient.php b/src/EppoClient.php index 53e7606..6a55366 100644 --- a/src/EppoClient.php +++ b/src/EppoClient.php @@ -582,7 +582,7 @@ public function fetchAndActivateConfiguration(): void $this->configurationLoader->reloadConfiguration(); } catch (HttpRequestException|InvalidApiKeyException|InvalidConfigurationException $e) { if ($this->isGracefulMode) { - error_log('[Eppo SDK] Error getting assignment: ' . $e->getMessage()); + error_log('[Eppo SDK] Error fetching configuration ' . $e->getMessage()); } throw EppoClientException::from($e); }