Commit ac64d7ca authored by Emiliano Balbuena's avatar Emiliano Balbuena

(wip): Strategy algorithms

1 merge request!2WIP: Clean up and strategies
Pipeline #105677760 passed with stage
in 1 minute and 2 seconds
<?php
/**
* NormalizedValue
*
* @author edgebal
*/
namespace Minds\UnleashClient\Helpers;
use lastguest\Murmur;
class NormalizedValue
{
/**
* Normalizes a value using Murmur3 algorithm hash and a normalizer modulus.
* Returns a value from 1 to 100 if ID is truthy, if not it returns 0.
* @param string $id
* @param string $groupId
* @param int $normalizer
* @return int
*/
public static function build(string $id, string $groupId, int $normalizer = 100): int
{
if (!$id) {
return 0;
}
return (Murmur::hash3_int("{$id}:{$groupId}") % $normalizer) + 1;
}
}
<?php
/**
* ApplicationHostnameStrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
class ApplicationHostnameStrategyAlgorithm implements StrategyAlgorithm
{
/**
* @inheritDoc
*/
public function isEnabled(Strategy $strategy, Context $context): bool
{
$parameters = $strategy->getParameters() ?? [];
$hostNamesList = $parameters['hostNames'] ?? '';
if (!$hostNamesList) {
return false;
}
$hostNames = array_map([$this, 'normalizeHostName'], explode(',', $hostNamesList));
return in_array(strtolower($context->getHostName()), $hostNames, true);
}
/**
* Normalizes host names for lookup
* @param string $hostName
* @return string
*/
protected function normalizeHostName(string $hostName): string
{
return trim(strtolower($hostName));
}
}
<?php
/**
* DefaultStrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
class DefaultStrategyAlgorithm implements StrategyAlgorithm
{
/**
* @inheritDoc
*/
public function isEnabled(Strategy $strategy, Context $context): bool
{
return true;
}
}
<?php
/**
* FlexibleRolloutStrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
use Minds\UnleashClient\Helpers\NormalizedValue;
class FlexibleRolloutStrategyAlgorithm implements StrategyAlgorithm
{
/**
* @inheritDoc
*/
public function isEnabled(Strategy $strategy, Context $context): bool
{
$parameters = $strategy->getParameters();
$percentage = $parameters['rollout'] ?? 0;
$stickiness = $parameters['stickiness'] ?? 'default';
$groupId = trim($parameters['groupId'] ?? '');
$userId = trim($context->getUserId() ?? '');
$sessionId = trim($context->getSessionId() ?? '');
switch ($stickiness) {
case 'userId':
$stickinessId = $userId;
break;
case 'sessionId':
$stickinessId = $sessionId;
break;
case 'random':
$stickinessId = sprintf("%s", mt_rand(1, 100));
break;
default:
$stickinessId = $userId ?: $sessionId ?: sprintf("%s", mt_rand(1, 100));
break;
}
if (!$stickinessId) {
return false;
}
$stickinessValue = NormalizedValue::build($stickinessId, $groupId);
return $percentage > 0 &&
$stickinessValue <= $percentage;
}
}
<?php
/**
* GradualRolloutRandomStrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
class GradualRolloutRandomStrategyAlgorithm implements StrategyAlgorithm
{
/**
* @inheritDoc
*/
public function isEnabled(Strategy $strategy, Context $context): bool
{
$parameters = $strategy->getParameters();
$percentage = $parameters['percentage'] ?? 0;
$random = mt_rand(1, 100);
return $percentage > 0 &&
$random <= $percentage;
}
}
<?php
/**
* GradualRolloutSessionIdStrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
use Minds\UnleashClient\Helpers\NormalizedValue;
class GradualRolloutSessionIdStrategyAlgorithm implements StrategyAlgorithm
{
/**
* @inheritDoc
*/
public function isEnabled(Strategy $strategy, Context $context): bool
{
$parameters = $strategy->getParameters();
$percentage = $parameters['percentage'] ?? 0;
$groupId = trim($parameters['groupId'] ?? '');
$sessionId = trim($context->getSessionId() ?? '');
if (!$sessionId) {
return false;
}
$sessionIdValue = NormalizedValue::build($sessionId, $groupId);
return $percentage > 0 &&
$sessionIdValue <= $percentage;
}
}
<?php
/**
* GradualRolloutUserIdStrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
use Minds\UnleashClient\Helpers\NormalizedValue;
class GradualRolloutUserIdStrategyAlgorithm implements StrategyAlgorithm
{
/**
* @inheritDoc
*/
public function isEnabled(Strategy $strategy, Context $context): bool
{
$parameters = $strategy->getParameters();
$percentage = $parameters['percentage'] ?? 0;
$groupId = trim($parameters['groupId'] ?? '');
$userId = trim($context->getUserId() ?? '');
if (!$userId) {
return false;
}
$userIdValue = NormalizedValue::build($userId, $groupId);
return $percentage > 0 &&
$userIdValue <= $percentage;
}
}
<?php
/**
* RemoteAddressStrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
class RemoteAddressStrategyAlgorithm implements StrategyAlgorithm
{
/**
* @inheritDoc
*/
public function isEnabled(Strategy $strategy, Context $context): bool
{
$parameters = $strategy->getParameters() ?? [];
$ipAddressesList = $parameters['IPs'] ?? '';
if (!$ipAddressesList) {
return false;
}
$ipAddresses = array_map([$this, 'normalizeIpAddress'], explode(',', $ipAddressesList));
return in_array(strtolower($context->getRemoteAddress()), $ipAddresses, true);
}
/**
* Normalizes IP addresses for lookup, using lowercase for IPv6
* @param string $ipAddress
* @return string
*/
protected function normalizeIpAddress(string $ipAddress): string
{
return trim(strtolower($ipAddress));
}
}
<?php
/**
* StrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
interface StrategyAlgorithm
{
/**
* Resolves a strategy using the context
* @param Strategy $strategy
* @param Context $context
* @return bool
*/
public function isEnabled(Strategy $strategy, Context $context): bool;
}
<?php
/**
* UserWithIdStrategyAlgorithm
*
* @author edgebal
*/
namespace Minds\UnleashClient\StrategyAlgorithms;
use Minds\UnleashClient\Entities\Context;
use Minds\UnleashClient\Entities\Strategy;
class UserWithIdStrategyAlgorithm implements StrategyAlgorithm
{
/**
* @inheritDoc
*/
public function isEnabled(Strategy $strategy, Context $context): bool
{
$parameters = $strategy->getParameters() ?? [];
$userIdsList = $parameters['userIds'] ?? '';
if (!$userIdsList) {
return false;
}
$userIds = array_map([$this, 'normalizeUserId'], explode(',', $userIdsList));
return in_array($context->getUserId(), $userIds, true);
}
/**
* Normalizes user IDs for lookup
* @param string $userId
* @return string
*/
protected function normalizeUserId(string $userId): string
{
return trim($userId);
}
}
Please register or to comment