From d5a4d419802a99d4e5a89273045450b600fbef13 Mon Sep 17 00:00:00 2001 From: Heihokon Date: Wed, 9 Jul 2025 14:02:38 -0500 Subject: [PATCH 1/2] fix: Prevent sending troubleshooting hits when not activated --- .../NoBatchingContinuousCachingStrategy.php | 55 +++++++++++++------ ...oBatchingContinuousCachingStrategyTest.php | 22 +++++--- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/Api/NoBatchingContinuousCachingStrategy.php b/src/Api/NoBatchingContinuousCachingStrategy.php index 2eec3226..bc311fbc 100644 --- a/src/Api/NoBatchingContinuousCachingStrategy.php +++ b/src/Api/NoBatchingContinuousCachingStrategy.php @@ -51,8 +51,8 @@ protected function onError(HitAbstract $hit): void protected function sendHit(HitAbstract $hit): void { $header = [ - FlagshipConstant::HEADER_CONTENT_TYPE => FlagshipConstant::HEADER_APPLICATION_JSON, - ]; + FlagshipConstant::HEADER_CONTENT_TYPE => FlagshipConstant::HEADER_APPLICATION_JSON, + ]; $requestBody = $hit->toApiKeys(); $now = $this->getNow(); @@ -68,8 +68,8 @@ protected function sendHit(HitAbstract $hit): void FlagshipConstant::TRACKING_MANAGER, FlagshipConstant::HIT_SENT_SUCCESS, [ - FlagshipConstant::SEND_HIT, - $this->getLogFormat(null, $url, $requestBody, $header, $this->getNow() - $now), + FlagshipConstant::SEND_HIT, + $this->getLogFormat(null, $url, $requestBody, $header, $this->getNow() - $now), ] ); } catch (Exception $exception) { @@ -79,12 +79,23 @@ protected function sendHit(HitAbstract $hit): void FlagshipConstant::TRACKING_MANAGER, FlagshipConstant::UNEXPECTED_ERROR_OCCURRED, [ - FlagshipConstant::SEND_HIT, - $this->getLogFormat($exception->getMessage(), $url, $requestBody, $header, $this->getNow() - $now), + FlagshipConstant::SEND_HIT, + $this->getLogFormat($exception->getMessage(), $url, $requestBody, $header, $this->getNow() - $now), ] ); $troubleshooting = new Troubleshooting(); - $troubleshooting->setLabel(TroubleshootingLabel::SEND_HIT_ROUTE_ERROR)->setLogLevel(LogLevel::ERROR)->setVisitorId($this->flagshipInstanceId)->setFlagshipInstanceId($this->flagshipInstanceId)->setTraffic(100)->setConfig($this->config)->setHttpRequestBody($requestBody)->setHttpRequestHeaders($header)->setHttpRequestMethod("POST")->setHttpRequestUrl($url)->setHttpResponseBody($exception->getMessage())->setHttpResponseTime($this->getNow() - $now); + $troubleshooting->setLabel(TroubleshootingLabel::SEND_HIT_ROUTE_ERROR) + ->setLogLevel(LogLevel::ERROR) + ->setFlagshipInstanceId($this->flagshipInstanceId) + ->setHttpRequestBody($requestBody) + ->setHttpRequestHeaders($header) + ->setHttpRequestMethod("POST") + ->setHttpRequestUrl($url) + ->setHttpResponseBody($exception->getMessage()) + ->setHttpResponseTime($this->getNow() - $now) + ->setTraffic(100)->setConfig($this->config) + ->setVisitorId($this->flagshipInstanceId) + ; $this->addTroubleshootingHit($troubleshooting); $this->sendTroubleshootingQueue(); } @@ -117,8 +128,8 @@ public function activateFlag(Activate $hit): void FlagshipConstant::TRACKING_MANAGER, FlagshipConstant::HIT_SENT_SUCCESS, [ - FlagshipConstant::SEND_ACTIVATE, - $this->getLogFormat(null, $url, $requestBody, $headers, $this->getNow() - $now), + FlagshipConstant::SEND_ACTIVATE, + $this->getLogFormat(null, $url, $requestBody, $headers, $this->getNow() - $now), ] ); } catch (Exception $exception) { @@ -128,22 +139,29 @@ public function activateFlag(Activate $hit): void FlagshipConstant::TRACKING_MANAGER, FlagshipConstant::UNEXPECTED_ERROR_OCCURRED, [ - FlagshipConstant::SEND_ACTIVATE, - $this->getLogFormat($exception->getMessage(), $url, $requestBody, $headers, $this->getNow() - $now), + FlagshipConstant::SEND_ACTIVATE, + $this->getLogFormat($exception->getMessage(), $url, $requestBody, $headers, $this->getNow() - $now), ] ); $troubleshooting = new Troubleshooting(); - $troubleshooting->setLabel(TroubleshootingLabel::SEND_ACTIVATE_HIT_ROUTE_ERROR)->setLogLevel(LogLevel::ERROR)->setVisitorId($this->flagshipInstanceId)->setFlagshipInstanceId($this->flagshipInstanceId)->setTraffic(100)->setConfig($this->config)->setHttpRequestBody($requestBody)->setHttpRequestHeaders($headers)->setHttpRequestMethod("POST")->setHttpRequestUrl($url)->setHttpResponseBody($exception->getMessage())->setHttpResponseTime($this->getNow() - $now); + $troubleshooting->setLabel(TroubleshootingLabel::SEND_ACTIVATE_HIT_ROUTE_ERROR) + ->setFlagshipInstanceId($this->flagshipInstanceId) + ->setHttpRequestBody($requestBody) + ->setHttpRequestHeaders($headers) + ->setHttpRequestMethod("POST") + ->setHttpRequestUrl($url) + ->setHttpResponseBody($exception->getMessage()) + ->setHttpResponseTime($this->getNow() - $now) + ->setLogLevel(LogLevel::ERROR) + ->setTraffic(100)->setConfig($this->config) + ->setVisitorId($this->flagshipInstanceId); $this->addTroubleshootingHit($troubleshooting); $this->sendTroubleshootingQueue(); } } - /** - * @param string $visitorId - * @return void - */ + protected function notConsent(string $visitorId): void { $keysToFlush = $this->commonNotConsent($visitorId); @@ -157,7 +175,10 @@ protected function notConsent(string $visitorId): void public function addTroubleshootingHit(Troubleshooting $hit): void { - $this->sendTroubleshooting($hit); + if (!$this->isTroubleshootingActivated()) { + return; + } + $this->sendTroubleshooting($hit); } public function addUsageHit(UsageHit $hit): void diff --git a/tests/Api/NoBatchingContinuousCachingStrategyTest.php b/tests/Api/NoBatchingContinuousCachingStrategyTest.php index 17f1d350..f48c44ad 100644 --- a/tests/Api/NoBatchingContinuousCachingStrategyTest.php +++ b/tests/Api/NoBatchingContinuousCachingStrategyTest.php @@ -3,18 +3,19 @@ namespace Flagship\Api; use Exception; -use Flagship\Config\DecisionApiConfig; -use Flagship\Enum\EventCategory; -use Flagship\Enum\FlagshipConstant; +use Flagship\Hit\Page; +use Flagship\Hit\Event; use Flagship\Hit\Activate; -use Flagship\Hit\ActivateBatch; use Flagship\Hit\UsageHit; -use Flagship\Hit\Event; use Flagship\Hit\HitAbstract; -use Flagship\Hit\Page; -use Flagship\Hit\Troubleshooting; use Flagship\Traits\LogTrait; +use Flagship\Hit\ActivateBatch; use PHPUnit\Framework\TestCase; +use Flagship\Enum\EventCategory; +use Flagship\Hit\Troubleshooting; +use Flagship\Enum\FlagshipConstant; +use Flagship\Config\DecisionApiConfig; +use PHPUnit\Framework\MockObject\MockObject; class NoBatchingContinuousCachingStrategyTest extends TestCase { @@ -527,6 +528,9 @@ public function testAddTroubleshootingHit() $httpClientMock = $this->getMockForAbstractClass('Flagship\Utils\HttpClientInterface'); + /** + * @var NoBatchingContinuousCachingStrategy| MockObject + */ $strategy = $this->getMockForAbstractClass( "Flagship\Api\NoBatchingContinuousCachingStrategy", [ @@ -537,12 +541,14 @@ public function testAddTroubleshootingHit() true, false, true, - ["sendTroubleshooting"] + ["sendTroubleshooting", 'isTroubleshootingActivated'] ); $troubleshooting = new Troubleshooting(); $troubleshooting->setConfig($config); + $strategy->expects($this->once())->method('isTroubleshootingActivated')->willReturn(true); + $strategy->expects($this->once())->method('sendTroubleshooting')->with($troubleshooting); $strategy->addTroubleshootingHit($troubleshooting); From aec73a33b6146b22a1ddb2d9fc86a0b5aa2bce74 Mon Sep 17 00:00:00 2001 From: Heihokon Date: Fri, 14 Nov 2025 13:54:23 -0600 Subject: [PATCH 2/2] fix: Update isAssoc method and enhance toApiKeys for better handling of null and boolean values --- src/Decision/BucketingManager.php | 56 +++++++++++++++---------------- src/Enum/FlagshipConstant.php | 2 +- src/Hit/Segment.php | 26 +++++++++----- tests/Api/TrackingManagerTest.php | 2 +- tests/Hit/SegmentTest.php | 46 ++++++++++++++++++------- 5 files changed, 81 insertions(+), 51 deletions(-) diff --git a/src/Decision/BucketingManager.php b/src/Decision/BucketingManager.php index 3f366579..c5972b93 100644 --- a/src/Decision/BucketingManager.php +++ b/src/Decision/BucketingManager.php @@ -77,13 +77,13 @@ public function __construct(HttpClientInterface $httpClient, BucketingConfig $co */ protected function sendContext(VisitorAbstract $visitor): void { - if (count($visitor->getContext()) <= self::NB_MIN_CONTEXT_KEYS || !$visitor->hasConsented()|| !$visitor->getHasContextBeenUpdated()) { + if (count($visitor->getContext()) <= self::NB_MIN_CONTEXT_KEYS || !$visitor->hasConsented() || !$visitor->getHasContextBeenUpdated()) { return; } $visitor->setHasContextBeenUpdated(false); - $segmentHit = new Segment($visitor->getContext()); + $segmentHit = new Segment($visitor->getContext(), $this->getConfig()); $visitor->sendHit($segmentHit); } @@ -127,14 +127,14 @@ protected function getThirdPartySegment(string $visitorId): array self::GET_THIRD_PARTY_SEGMENT, FlagshipConstant::UNEXPECTED_ERROR_OCCURRED, [ - self::THIRD_PARTY_SEGMENT, - $this->getLogFormat( - $exception->getMessage(), - $url, - [], - [], - $this->getNow() - $now - ), + self::THIRD_PARTY_SEGMENT, + $this->getLogFormat( + $exception->getMessage(), + $url, + [], + [], + $this->getNow() - $now + ), ] ); } @@ -289,14 +289,14 @@ private function getVisitorCampaigns( $visitor ); $visitorCampaigns[] = [ - FlagshipField::FIELD_ID => $campaignId, - FlagshipField::FIELD_NANE => $campaignName, - FlagshipField::FIELD_SLUG => $slug, - FlagshipField::FIELD_VARIATION_GROUP_ID => $variationGroup[FlagshipField::FIELD_ID], - FlagshipField::FIELD_VARIATION_GROUP_NAME => $variationGroup[FlagshipField::FIELD_NANE] ?? null, - FlagshipField::FIELD_VARIATION => $variations, - FlagshipField::FIELD_CAMPAIGN_TYPE => $campaignType, - ]; + FlagshipField::FIELD_ID => $campaignId, + FlagshipField::FIELD_NANE => $campaignName, + FlagshipField::FIELD_SLUG => $slug, + FlagshipField::FIELD_VARIATION_GROUP_ID => $variationGroup[FlagshipField::FIELD_ID], + FlagshipField::FIELD_VARIATION_GROUP_NAME => $variationGroup[FlagshipField::FIELD_NANE] ?? null, + FlagshipField::FIELD_VARIATION => $variations, + FlagshipField::FIELD_CAMPAIGN_TYPE => $campaignType, + ]; break; } } @@ -352,11 +352,11 @@ private function getVariation(array $variationGroup, VisitorAbstract $visitor): continue; } $visitorVariation = [ - FlagshipField::FIELD_ID => $newVariation[FlagshipField::FIELD_ID], - FlagshipField::FIELD_MODIFICATIONS => $newVariation[FlagshipField::FIELD_MODIFICATIONS], - FlagshipField::FIELD_REFERENCE => !empty($newVariation[FlagshipField::FIELD_REFERENCE]), - FlagshipField::FIELD_NANE => $newVariation[FlagshipField::FIELD_NANE] ?? null, - ]; + FlagshipField::FIELD_ID => $newVariation[FlagshipField::FIELD_ID], + FlagshipField::FIELD_MODIFICATIONS => $newVariation[FlagshipField::FIELD_MODIFICATIONS], + FlagshipField::FIELD_REFERENCE => !empty($newVariation[FlagshipField::FIELD_REFERENCE]), + FlagshipField::FIELD_NANE => $newVariation[FlagshipField::FIELD_NANE] ?? null, + ]; break; } @@ -367,11 +367,11 @@ private function getVariation(array $variationGroup, VisitorAbstract $visitor): $totalAllocation += $variation[FlagshipField::FIELD_ALLOCATION]; if ($hashAllocation < $totalAllocation) { $visitorVariation = [ - FlagshipField::FIELD_ID => $variation[FlagshipField::FIELD_ID], - FlagshipField::FIELD_MODIFICATIONS => $variation[FlagshipField::FIELD_MODIFICATIONS], - FlagshipField::FIELD_REFERENCE => !empty($variation[FlagshipField::FIELD_REFERENCE]), - FlagshipField::FIELD_NANE => $variation[FlagshipField::FIELD_NANE] ?? null, - ]; + FlagshipField::FIELD_ID => $variation[FlagshipField::FIELD_ID], + FlagshipField::FIELD_MODIFICATIONS => $variation[FlagshipField::FIELD_MODIFICATIONS], + FlagshipField::FIELD_REFERENCE => !empty($variation[FlagshipField::FIELD_REFERENCE]), + FlagshipField::FIELD_NANE => $variation[FlagshipField::FIELD_NANE] ?? null, + ]; break; } } diff --git a/src/Enum/FlagshipConstant.php b/src/Enum/FlagshipConstant.php index 270fb673..562bcd6f 100644 --- a/src/Enum/FlagshipConstant.php +++ b/src/Enum/FlagshipConstant.php @@ -54,7 +54,7 @@ class FlagshipConstant /** * SDK version */ - public const SDK_VERSION = "4.1.1"; + public const SDK_VERSION = "4.1.2"; public const GET_FLAG = 'GET_FLAG'; diff --git a/src/Hit/Segment.php b/src/Hit/Segment.php index 438e72fa..a6748279 100644 --- a/src/Hit/Segment.php +++ b/src/Hit/Segment.php @@ -2,8 +2,9 @@ namespace Flagship\Hit; -use Flagship\Enum\FlagshipConstant; use Flagship\Enum\HitType; +use Flagship\Config\FlagshipConfig; +use Flagship\Enum\FlagshipConstant; class Segment extends HitAbstract { @@ -18,7 +19,7 @@ public static function getClassName(): string /** * @var array */ - protected array $sl; + protected array $sl = []; /** * @return array @@ -45,9 +46,10 @@ public function setSl(array $sl): static /** * @param array $sl */ - public function __construct(array $sl) + public function __construct(array $sl, FlagshipConfig $config) { parent::__construct(HitType::SEGMENT); + $this->setConfig($config); $this->setSl($sl); } @@ -57,9 +59,7 @@ public function __construct(array $sl) */ protected function isAssoc(array $array): bool { - $keys = array_keys($array); - - return array_keys($keys) !== $keys; + return !array_is_list($array); } /** @@ -68,7 +68,17 @@ protected function isAssoc(array $array): bool public function toApiKeys(): array { $arrayParent = parent::toApiKeys(); - $arrayParent[FlagshipConstant::SL_API_ITEM] = $this->getSl(); + $apiContext = array_map(function ($value) { + if ($value === null) { + return 'null'; + } + if (is_bool($value)) { + return $value ? 'true' : 'false'; + } + return strval($value); + }, $this->getSl()); + + $arrayParent[FlagshipConstant::SL_API_ITEM] = $apiContext; return $arrayParent; } @@ -77,7 +87,7 @@ public function toApiKeys(): array */ public function isReady(): bool { - return parent::isReady() && $this->getSl(); + return parent::isReady() && $this->getSl() && count($this->getSl()) > 0; } /** diff --git a/tests/Api/TrackingManagerTest.php b/tests/Api/TrackingManagerTest.php index b544cad1..7f01fe12 100644 --- a/tests/Api/TrackingManagerTest.php +++ b/tests/Api/TrackingManagerTest.php @@ -180,7 +180,7 @@ public function testLookupHits() $screen = new Screen("home"); $screen->setConfig($config)->setVisitorId($visitorId)->setKey("$visitorId:key4"); - $segment = new Segment(["key" => "value"]); + $segment = new Segment(["key" => "value"], $config); $segment->setConfig($config)->setVisitorId($visitorId)->setKey("$visitorId:key5"); $activate = new Activate("varGrid", "varId"); diff --git a/tests/Hit/SegmentTest.php b/tests/Hit/SegmentTest.php index 517cc651..9ec8a63a 100644 --- a/tests/Hit/SegmentTest.php +++ b/tests/Hit/SegmentTest.php @@ -15,14 +15,18 @@ public function testConstructor() $visitorId = "visitorId"; $context = [ - "key1" => "value1", - "key2" => 1, - "key3" => true, - ]; + "key1" => "value1", + "key2" => 1, + "key3" => true, + "key4" => null, + "key5" => false, + ]; $config = new DecisionApiConfig($envId); - $segment = new Segment($context); + + + $segment = new Segment($context, $config); $segment->setConfig($config)->setVisitorId($visitorId); $segment->setSl(["key"]); @@ -30,19 +34,35 @@ public function testConstructor() $this->assertSame($context, $segment->getSl()); $segmentArray = [ - FlagshipConstant::VISITOR_ID_API_ITEM => $visitorId, - FlagshipConstant::DS_API_ITEM => FlagshipConstant::SDK_APP, - FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $envId, - FlagshipConstant::T_API_ITEM => HitType::SEGMENT->value, - FlagshipConstant::CUSTOMER_UID => null, - FlagshipConstant::QT_API_ITEM => 0.0, - FlagshipConstant::SL_API_ITEM => $context, - ]; + FlagshipConstant::VISITOR_ID_API_ITEM => $visitorId, + FlagshipConstant::DS_API_ITEM => FlagshipConstant::SDK_APP, + FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $envId, + FlagshipConstant::T_API_ITEM => HitType::SEGMENT->value, + FlagshipConstant::CUSTOMER_UID => null, + FlagshipConstant::QT_API_ITEM => 0.0, + FlagshipConstant::SL_API_ITEM => [ + "key1" => "value1", + "key2" => "1", + "key3" => "true", + "key4" => "null", + "key5" => "false", + ] + ]; $this->assertSame($segmentArray, $segment->toApiKeys()); $this->assertTrue($segment->isReady()); $this->assertSame(Segment::ERROR_MESSAGE, $segment->getErrorMessage()); + + $segment1 = new Segment([], $config); + + $segment1->setConfig($config)->setVisitorId($visitorId); + + $segmentArray[FlagshipConstant::SL_API_ITEM] = []; + + $this->assertSame($segmentArray, $segment1->toApiKeys()); + + $this->assertFalse($segment1->isReady()); } }