diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 66c75b5..91180b1 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['8.0','8.2','8.3'] + php-versions: ['8.0','8.2','8.3', '8.4'] steps: - uses: actions/checkout@v3 diff --git a/rector.php b/rector.php index 5f3c0c8..ab6d739 100644 --- a/rector.php +++ b/rector.php @@ -14,7 +14,9 @@ SimplifyEmptyCheckOnEmptyArrayRector::class, ]) ->withSets([ - SetList::PHP_80, + SetList::PHP_82, + SetList::PHP_83, + SetList::PHP_84, SetList::DEAD_CODE, SetList::CODE_QUALITY, SetList::CODING_STYLE, diff --git a/src/Endpoint/Data/Filters/AbstractPredefinedFilter.php b/src/Endpoint/Data/Filters/AbstractPredefinedFilter.php new file mode 100644 index 0000000..86d87d0 --- /dev/null +++ b/src/Endpoint/Data/Filters/AbstractPredefinedFilter.php @@ -0,0 +1,27 @@ +getOperator(), + $arguments[0] ?? '', + ]; + } + + parent::__construct($arguments); + } + + public function compile(): array + { + return [ + $this->getOperator() => $this->getValue(), + ]; + } +} diff --git a/src/Endpoint/Data/Filters/Creator.php b/src/Endpoint/Data/Filters/Creator.php new file mode 100644 index 0000000..4faaed3 --- /dev/null +++ b/src/Endpoint/Data/Filters/Creator.php @@ -0,0 +1,8 @@ + Equals::class, 'notEquals' => NotEquals::class, 'starts' => Starts::class, @@ -85,15 +94,20 @@ abstract class AbstractExpression implements FilterInterface, ExpressionInterfac 'greaterThanOrEquals' => GreaterThanOrEqual::class, 'between' => Between::class, 'dateBetween' => DateBetween::class, + 'inRadiusFromCoords' => InRadiusFromCoords::class, + 'inRadiusFromZip' => InRadiusFromZip::class, + 'favorite' => Favorite::class, + 'following' => Following::class, + 'owner' => Owner::class, + 'tracker' => Tracker::class, + 'creator' => Creator::class, ]; - /** - * @var array - */ - protected $expressions = [ + protected array $expressions = [ 'and' => AndExpression::class, 'or' => OrExpression::class, 'date' => DateExpression::class, + 'distance' => DistanceExpression::class, ]; /** @@ -126,7 +140,7 @@ public function __call($name, $arguments) * Sets Parent Expression to allow for nested tree structure * @return $this */ - public function setParentExpression(AbstractExpression $Expression) + public function setParentExpression(AbstractExpression $Expression): static { $this->parentExpression = $Expression; return $this; @@ -156,7 +170,7 @@ public function compile(): array /** * @inheritDoc */ - public function clear() + public function clear(): static { $this->filters = []; return $this; diff --git a/src/Endpoint/Data/Filters/Expression/DateExpression.php b/src/Endpoint/Data/Filters/Expression/DateExpression.php index 68fed69..7322808 100644 --- a/src/Endpoint/Data/Filters/Expression/DateExpression.php +++ b/src/Endpoint/Data/Filters/Expression/DateExpression.php @@ -16,7 +16,7 @@ use Sugarcrm\REST\Endpoint\Data\Filters\Operator\GreaterThanOrEqual; use Sugarcrm\REST\Endpoint\Data\Filters\Operator\DateBetween; use Sugarcrm\REST\Endpoint\Data\Filters\Operator\DateRange; -use Sugarcrm\REST\Exception\Filter\MissingFieldForDateExpression; +use Sugarcrm\REST\Exception\Filter\MissingFieldForFilterExpression; use Sugarcrm\REST\Exception\Filter\UnknownFilterOperator; /** @@ -54,9 +54,9 @@ class DateExpression extends AbstractExpression { public const OPERATOR = ''; - protected $dateField; + protected string $dateField; - protected $ranges = [ + protected array $ranges = [ 'yesterday' => 'yesterday', 'today' => 'today', 'tomorrow' => 'tomorrow', @@ -72,10 +72,7 @@ class DateExpression extends AbstractExpression 'nextYear' => 'next_year', ]; - /** - * @var array - */ - protected $operators = [ + protected array $operators = [ 'equals' => Equals::class, 'notEquals' => NotEquals::class, 'isNull' => IsNull::class, @@ -94,10 +91,7 @@ class DateExpression extends AbstractExpression 'between' => DateBetween::class, ]; - /** - * @var array - */ - protected $expressions = []; + protected array $expressions = []; /** * DateExpression constructor. @@ -114,21 +108,32 @@ public function __construct(array $arguments = []) * @param $field * @return $this */ - public function field($field): self + public function field(string $field): self { $this->dateField = $field; return $this; } + protected function isDateRange(string $name): string|false + { + $range = false; + if (array_key_exists($name, $this->ranges)) { + $range = $this->ranges[$name]; + } elseif (in_array($name, $this->ranges)) { + $range = $name; + } + + return $range; + } + public function __call($name, $arguments) { if (empty($this->dateField)) { - throw new MissingFieldForDateExpression(); + throw new MissingFieldForFilterExpression(); } $args = [$this->dateField]; - if (array_key_exists($name, $this->ranges)) { - $range = $this->ranges[$name]; + if ($range = $this->isDateRange($name)) { $args[] = $range; $Op = new DateRange($args); $this->filters[0] = $Op; diff --git a/src/Endpoint/Data/Filters/Expression/DistanceExpression.php b/src/Endpoint/Data/Filters/Expression/DistanceExpression.php new file mode 100644 index 0000000..26f295a --- /dev/null +++ b/src/Endpoint/Data/Filters/Expression/DistanceExpression.php @@ -0,0 +1,75 @@ + InRadiusFromZip::class, + 'radiusFromCoords' => InRadiusFromCoords::class, + ]; + + protected array $expressions = []; + + protected string $distanceField; + + /** + * DateExpression constructor. + */ + public function __construct(array $arguments = []) + { + if (isset($arguments[0])) { + $this->field($arguments[0]); + } + } + + /** + * Set the field that date expression is against + * @param $field + * @return $this + */ + public function field(string $field): self + { + $this->distanceField = $field; + return $this; + } + + public function __call($name, $arguments) + { + if (empty($this->distanceField)) { + throw new MissingFieldForFilterExpression(); + } + + $args = [$this->distanceField]; + if (array_key_exists($name, $this->operators)) { + $args = array_merge($args, $arguments); + $Operator = $this->operators[$name]; + $O = new $Operator($args); + $this->filters[0] = $O; + return $this; + } + + throw new UnknownFilterOperator([$name]); + } + + /** + * Human Friendly Expression End, allow you to traverse back up the Filter expression + * @codeCoverageIgnore + */ + public function endDistance(): AbstractExpression + { + return $this->getParentExpression(); + } +} diff --git a/src/Endpoint/Data/Filters/Expression/ExpressionInterface.php b/src/Endpoint/Data/Filters/Expression/ExpressionInterface.php index 47c5e18..145dad8 100644 --- a/src/Endpoint/Data/Filters/Expression/ExpressionInterface.php +++ b/src/Endpoint/Data/Filters/Expression/ExpressionInterface.php @@ -6,20 +6,17 @@ namespace Sugarcrm\REST\Endpoint\Data\Filters\Expression; +use Sugarcrm\REST\Endpoint\Data\Filters\FilterInterface; + /** * The Expression Interface defines the basic API needed for an Expression object used in the Filter API Data Layer * @package Sugarcrm\REST\Endpoint\Data\Filters\Expression **/ -interface ExpressionInterface +interface ExpressionInterface extends FilterInterface { - /** - * Compiles the Filter Expression into an array to be passed to Sugar Filter API - */ - public function compile(): array; - /** * Clear out Filters included in Expression * @return $this */ - public function clear(); + public function clear(): static; } diff --git a/src/Endpoint/Data/Filters/Favorite.php b/src/Endpoint/Data/Filters/Favorite.php new file mode 100644 index 0000000..fe8f086 --- /dev/null +++ b/src/Endpoint/Data/Filters/Favorite.php @@ -0,0 +1,8 @@ +operator ?? static::OPERATOR; + } + /** * Set the field on the Operator * @param $field string * @return $this */ - public function setField($field) + public function setField(string $field): static { - $this->field = (string) $field; + $this->field = $field; return $this; } /** * Get the field configured on the Operator - * @return string */ - public function getField() + public function getField(): string { - return $this->field; + return $this->field ?? ''; } /** @@ -73,7 +74,7 @@ public function getField() * @param $value * @return $this */ - public function setValue($value) + public function setValue(mixed $value): static { $this->value = $value; return $this; @@ -81,11 +82,10 @@ public function setValue($value) /** * Get the value configure on the Operator - * @return mixed */ - public function getValue() + public function getValue(): mixed { - return $this->value; + return $this->value ?? null; } /** @@ -95,7 +95,7 @@ public function compile(): array { return [ $this->getField() => [ - static::$_OPERATOR => $this->getValue(), + $this->getOperator() => $this->getValue(), ], ]; } diff --git a/src/Endpoint/Data/Filters/Operator/Between.php b/src/Endpoint/Data/Filters/Operator/Between.php index 9f94c3c..cab9b69 100644 --- a/src/Endpoint/Data/Filters/Operator/Between.php +++ b/src/Endpoint/Data/Filters/Operator/Between.php @@ -12,6 +12,4 @@ class Between extends AbstractOperator { public const OPERATOR = '$between'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/Contains.php b/src/Endpoint/Data/Filters/Operator/Contains.php index 48256d7..b145a47 100644 --- a/src/Endpoint/Data/Filters/Operator/Contains.php +++ b/src/Endpoint/Data/Filters/Operator/Contains.php @@ -12,6 +12,4 @@ class Contains extends AbstractOperator { public const OPERATOR = '$contains'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/DateBetween.php b/src/Endpoint/Data/Filters/Operator/DateBetween.php index ffd3eee..f2b74ea 100644 --- a/src/Endpoint/Data/Filters/Operator/DateBetween.php +++ b/src/Endpoint/Data/Filters/Operator/DateBetween.php @@ -12,6 +12,4 @@ class DateBetween extends AbstractOperator { public const OPERATOR = '$dateBetween'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/DateRange.php b/src/Endpoint/Data/Filters/Operator/DateRange.php index 2152eef..58d16f9 100644 --- a/src/Endpoint/Data/Filters/Operator/DateRange.php +++ b/src/Endpoint/Data/Filters/Operator/DateRange.php @@ -12,6 +12,4 @@ class DateRange extends AbstractOperator { public const OPERATOR = '$dateRange'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/Ends.php b/src/Endpoint/Data/Filters/Operator/Ends.php index 848a2b6..8c404ad 100644 --- a/src/Endpoint/Data/Filters/Operator/Ends.php +++ b/src/Endpoint/Data/Filters/Operator/Ends.php @@ -12,6 +12,4 @@ class Ends extends AbstractOperator { public const OPERATOR = '$ends'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/Equals.php b/src/Endpoint/Data/Filters/Operator/Equals.php index 089aca8..65d6825 100644 --- a/src/Endpoint/Data/Filters/Operator/Equals.php +++ b/src/Endpoint/Data/Filters/Operator/Equals.php @@ -12,6 +12,4 @@ class Equals extends AbstractOperator { public const OPERATOR = '$equals'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/GreaterThan.php b/src/Endpoint/Data/Filters/Operator/GreaterThan.php index 0dc9427..0e3cc2e 100644 --- a/src/Endpoint/Data/Filters/Operator/GreaterThan.php +++ b/src/Endpoint/Data/Filters/Operator/GreaterThan.php @@ -12,6 +12,4 @@ class GreaterThan extends AbstractOperator { public const OPERATOR = '$gt'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/GreaterThanOrEqual.php b/src/Endpoint/Data/Filters/Operator/GreaterThanOrEqual.php index c19fe07..21c48c4 100644 --- a/src/Endpoint/Data/Filters/Operator/GreaterThanOrEqual.php +++ b/src/Endpoint/Data/Filters/Operator/GreaterThanOrEqual.php @@ -12,6 +12,4 @@ class GreaterThanOrEqual extends AbstractOperator { public const OPERATOR = '$gte'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/In.php b/src/Endpoint/Data/Filters/Operator/In.php index 1639c29..3b2c9c8 100644 --- a/src/Endpoint/Data/Filters/Operator/In.php +++ b/src/Endpoint/Data/Filters/Operator/In.php @@ -12,6 +12,4 @@ class In extends AbstractOperator { public const OPERATOR = '$in'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/InRadiusFromCoords.php b/src/Endpoint/Data/Filters/Operator/InRadiusFromCoords.php new file mode 100644 index 0000000..e131fb5 --- /dev/null +++ b/src/Endpoint/Data/Filters/Operator/InRadiusFromCoords.php @@ -0,0 +1,138 @@ + 1) { + $this->configureParams($params); + } + } + } + + public function setLatitude(float $latitude): static + { + $this->latitude = $latitude; + return $this; + } + + public function setLongitude(float $longitude): static + { + $this->longitude = $longitude; + return $this; + } + + public function setCoordinates(array $coordinates): static + { + $latitude = $coordinates[self::PARAM_LATITUDE] ?? $coordinates[0] ?? null; + if ($latitude) { + $this->setLatitude((float) $latitude); + } + + $longitude = $coordinates[self::PARAM_LONGITUDE] ?? $coordinates[1] ?? null; + if ($longitude) { + $this->setLongitude((float) $longitude); + } + + return $this; + } + + private function makeValue(): array + { + return [ + self::PARAM_LATITUDE => $this->latitude ?? 0, + self::PARAM_LONGITUDE => $this->longitude ?? 0, + self::PARAM_RADIUS => $this->radius ?? 0, + self::PARAM_UNIT => $this->unitType ?? 'km', + ]; + } + + private function configureParams(array $params): static + { + $i = 0; + if (isset($params[$i]) && is_array($params[$i])) { + // Coordinate array + $this->setCoordinates($params[$i]); + if ($params[1] === null) { + // longitude param is null, so radius is at i+2 + $i++; + } + } else { + $this->setCoordinates($params); + $longI = 1; + if (isset($params[$longI]) && $params[$longI] === $this->longitude) { + $i++; + } + } + + $i++; + $radius = $params[self::PARAM_RADIUS] ?? $params[$i] ?? null; + if ($radius) { + $this->setRadius((float) $radius); + } + + $unitType = $params[self::PARAM_UNIT] ?? $params[$i + 1] ?? null; + if (!empty($unitType)) { + $this->setUnitType($unitType); + } + + $this->value = $this->makeValue(); + return $this; + } + + public function getValue(): mixed + { + if (empty($this->value)) { + $this->value = $this->makeValue(); + } + + return parent::getValue(); + } + + /** + * Set the Value on the Operator + * @param $value + * @return $this + */ + public function setValue(mixed $value): static + { + if (is_array($value)) { + $this->configureParams($value); + } + + $this->value = $this->makeValue(); + return $this; + } + + /** + * @inheritdoc + */ + public function compile(): array + { + return [ + $this->getField() => [ + $this->getOperator() => $this->getValue(), + ], + ]; + } +} diff --git a/src/Endpoint/Data/Filters/Operator/InRadiusFromZip.php b/src/Endpoint/Data/Filters/Operator/InRadiusFromZip.php new file mode 100644 index 0000000..48263c2 --- /dev/null +++ b/src/Endpoint/Data/Filters/Operator/InRadiusFromZip.php @@ -0,0 +1,116 @@ + 1) { + $this->configureParams($params); + } + } + } + + public function setZipCode(string $zipCode): static + { + $this->zipCode = $zipCode; + return $this; + } + + public function setCountry(string $country): static + { + $this->country = $country; + return $this; + } + + private function makeValue(): array + { + return [ + self::PARAM_ZIP => $this->zipCode ?? 0, + self::PARAM_COUNTRY => $this->country ?? "US", + InRadiusFromCoords::PARAM_RADIUS => $this->radius ?? 0, + InRadiusFromCoords::PARAM_UNIT => $this->unitType ?? 'km', + ]; + } + + private function configureParams(array $params): static + { + $zip = $params[self::PARAM_ZIP] ?? $params[0] ?? null; + if ($zip) { + $this->setZipCode((string) $zip); + } + + $country = $params[self::PARAM_COUNTRY] ?? $params[1] ?? null; + if ($country) { + $this->setCountry((string) $country); + } + + $radius = $params[InRadiusFromCoords::PARAM_RADIUS] ?? $params[2] ?? null; + if ($radius) { + $this->setRadius((float) $radius); + } + + $unitType = $params[InRadiusFromCoords::PARAM_UNIT] ?? $params[3] ?? null; + if (!empty($unitType)) { + $this->setUnitType($unitType); + } + + $this->value = $this->makeValue(); + return $this; + } + + public function getValue(): mixed + { + if (empty($this->value)) { + $this->value = $this->makeValue(); + } + + return parent::getValue(); + } + + /** + * Set the Value on the Operator + * @param $value + * @return $this + */ + public function setValue(mixed $value): static + { + if (is_array($value)) { + $this->configureParams($value); + } else { + $this->setZipCode((string) $value); + } + + $this->value = $this->makeValue(); + return $this; + } + + /** + * @inheritdoc + */ + public function compile(): array + { + return [ + $this->getField() => [ + $this->getOperator() => $this->getValue(), + ], + ]; + } + + +} diff --git a/src/Endpoint/Data/Filters/Operator/IsNull.php b/src/Endpoint/Data/Filters/Operator/IsNull.php index 1558839..d4b55da 100644 --- a/src/Endpoint/Data/Filters/Operator/IsNull.php +++ b/src/Endpoint/Data/Filters/Operator/IsNull.php @@ -13,9 +13,7 @@ class IsNull extends AbstractOperator { public const OPERATOR = '$is_null'; - protected static $_OPERATOR = self::OPERATOR; - - public function setValue($value): self + public function setValue(mixed $value): static { $this->value = null; return $this; @@ -24,7 +22,7 @@ public function setValue($value): self public function compile(): array { return [ - $this->getField() => [static::$_OPERATOR], + $this->getField() => [ $this->getOperator() ], ]; } } diff --git a/src/Endpoint/Data/Filters/Operator/LessThan.php b/src/Endpoint/Data/Filters/Operator/LessThan.php index 685038c..f8e852c 100644 --- a/src/Endpoint/Data/Filters/Operator/LessThan.php +++ b/src/Endpoint/Data/Filters/Operator/LessThan.php @@ -12,6 +12,4 @@ class LessThan extends AbstractOperator { public const OPERATOR = '$lt'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/LessThanOrEqual.php b/src/Endpoint/Data/Filters/Operator/LessThanOrEqual.php index 1a9a3d4..ea9d4a6 100644 --- a/src/Endpoint/Data/Filters/Operator/LessThanOrEqual.php +++ b/src/Endpoint/Data/Filters/Operator/LessThanOrEqual.php @@ -12,6 +12,4 @@ class LessThanOrEqual extends AbstractOperator { public const OPERATOR = '$lte'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/MapFilterOperatorTrait.php b/src/Endpoint/Data/Filters/Operator/MapFilterOperatorTrait.php new file mode 100644 index 0000000..fe664f5 --- /dev/null +++ b/src/Endpoint/Data/Filters/Operator/MapFilterOperatorTrait.php @@ -0,0 +1,22 @@ +radius = $radius; + return $this; + } + + public function setUnitType(string $unitType): static + { + $this->unitType = $unitType; + return $this; + } +} diff --git a/src/Endpoint/Data/Filters/Operator/NotEquals.php b/src/Endpoint/Data/Filters/Operator/NotEquals.php index a1778de..ca32109 100644 --- a/src/Endpoint/Data/Filters/Operator/NotEquals.php +++ b/src/Endpoint/Data/Filters/Operator/NotEquals.php @@ -12,6 +12,4 @@ class NotEquals extends AbstractOperator { public const OPERATOR = '$not_equals'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/NotIn.php b/src/Endpoint/Data/Filters/Operator/NotIn.php index db5912f..12d08af 100644 --- a/src/Endpoint/Data/Filters/Operator/NotIn.php +++ b/src/Endpoint/Data/Filters/Operator/NotIn.php @@ -13,6 +13,4 @@ class NotIn extends AbstractOperator { public const OPERATOR = '$not_in'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/NotNull.php b/src/Endpoint/Data/Filters/Operator/NotNull.php index fa32177..d4bd8c2 100644 --- a/src/Endpoint/Data/Filters/Operator/NotNull.php +++ b/src/Endpoint/Data/Filters/Operator/NotNull.php @@ -12,6 +12,4 @@ class NotNull extends IsNull { public const OPERATOR = '$not_null'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Operator/Starts.php b/src/Endpoint/Data/Filters/Operator/Starts.php index 9f42fbd..12b4d83 100644 --- a/src/Endpoint/Data/Filters/Operator/Starts.php +++ b/src/Endpoint/Data/Filters/Operator/Starts.php @@ -12,6 +12,4 @@ class Starts extends AbstractOperator { public const OPERATOR = '$starts'; - - protected static $_OPERATOR = self::OPERATOR; } diff --git a/src/Endpoint/Data/Filters/Owner.php b/src/Endpoint/Data/Filters/Owner.php new file mode 100644 index 0000000..06c3acd --- /dev/null +++ b/src/Endpoint/Data/Filters/Owner.php @@ -0,0 +1,8 @@ +assertEmpty($Operator->getField()); - $this->assertEmpty($Operator->getValue()); $Operator = new Contains(['foo']); $this->assertEquals('foo', $Operator->getField()); $this->assertEmpty($Operator->getValue()); diff --git a/tests/Endpoint/Data/Filters/DateExpressionTest.php b/tests/Endpoint/Data/Filters/DateExpressionTest.php index 2f4782b..99b3bcd 100644 --- a/tests/Endpoint/Data/Filters/DateExpressionTest.php +++ b/tests/Endpoint/Data/Filters/DateExpressionTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase; use Sugarcrm\REST\Exception\Filter\UnknownFilterOperator; -use Sugarcrm\REST\Exception\Filter\MissingFieldForDateExpression; +use Sugarcrm\REST\Exception\Filter\MissingFieldForFilterExpression; use Sugarcrm\REST\Endpoint\Data\Filters\Expression\DateExpression; /** @@ -54,7 +54,6 @@ public function testField(): void $this->assertEquals('test', $dateField->getValue($Date)); $Date = new DateExpression(); - $this->assertEmpty($dateField->getValue($Date)); $this->assertEquals($Date, $Date->field('test')); $this->assertEquals('test', $dateField->getValue($Date)); } @@ -103,7 +102,6 @@ public function testCall(): void /** * @covers ::__call - * @expectedException Sugarcrm\REST\Exception\Filter\UnknownFilterOperator */ public function testUnknownFilterOperatorException(): void { @@ -116,13 +114,12 @@ public function testUnknownFilterOperatorException(): void /** * @covers::__call - * @expectedException Sugarcrm\REST\Exception\Filter\MissingFieldForDateExpression */ public function testMissingFieldException(): void { $Expression = new DateExpression(); - $this->expectException(MissingFieldForDateExpression::class); - $this->expectExceptionMessage("Field not configured on DateExpression"); + $this->expectException(MissingFieldForFilterExpression::class); + $this->expectExceptionMessage("Field not configured on Filter Expression"); $Expression->yesterday(); } } diff --git a/tests/Endpoint/Data/Filters/Expression/DistanceExpressionTest.php b/tests/Endpoint/Data/Filters/Expression/DistanceExpressionTest.php new file mode 100644 index 0000000..f3f79c9 --- /dev/null +++ b/tests/Endpoint/Data/Filters/Expression/DistanceExpressionTest.php @@ -0,0 +1,113 @@ +field('address')->radiusFromZip('90210', 'US', 25, 'mi'); + $expected = [ + 'address' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => '90210', + 'countries' => 'US', + 'radius' => 25.0, + 'unitType' => 'mi', + ], + ], + ]; + $expr->compile(); + $this->assertEquals($expected, $expr->compile()[0]); + } + + public function testRadiusFromZipWithDefaults(): void + { + $expr = new DistanceExpression(); + $expr->field('address')->radiusFromZip('12345'); + $expected = [ + 'address' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => '12345', + 'countries' => 'US', + 'radius' => 0.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $expr->compile()[0]); + } + + public function testRadiusFromCoordsWithAllArguments(): void + { + $expr = new DistanceExpression(); + $expr->field('geo')->radiusFromCoords(37.77, -122.41, 10, 'mi'); + $expected = [ + 'geo' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 37.77, + 'longitude' => -122.41, + 'radius' => 10.0, + 'unitType' => 'mi', + ], + ], + ]; + $this->assertEquals($expected, $expr->compile()[0]); + } + + public function testRadiusFromCoordsWithArrayCoordinates(): void + { + $expr = new DistanceExpression(); + $expr->field('geo')->radiusFromCoords([51.5, -0.12], null, 15, 'km'); + $expected = [ + 'geo' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 51.5, + 'longitude' => -0.12, + 'radius' => 15.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $expr->compile()[0]); + } + + public function testRadiusFromCoordsWithDefaults(): void + { + $expr = new DistanceExpression(); + $expr->field('geo')->radiusFromCoords(10.1, 20.2); + $expected = [ + 'geo' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 10.1, + 'longitude' => 20.2, + 'radius' => 0.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $expr->compile()[0]); + } + + public function testMissingFieldThrowsException(): void + { + $this->expectException(MissingFieldForFilterExpression::class); + $expr = new DistanceExpression(); + $expr->radiusFromZip('90210'); + } + + public function testUnknownOperatorThrowsException(): void + { + $this->expectException(UnknownFilterOperator::class); + $expr = new DistanceExpression(); + $expr->field('address')->notARealOperator('foo'); + } +} diff --git a/tests/Endpoint/Data/Filters/Operator/InRadiusFromCoordsTest.php b/tests/Endpoint/Data/Filters/Operator/InRadiusFromCoordsTest.php new file mode 100644 index 0000000..bbb0c1c --- /dev/null +++ b/tests/Endpoint/Data/Filters/Operator/InRadiusFromCoordsTest.php @@ -0,0 +1,145 @@ +setField('location') + ->setLatitude(37.7749) + ->setLongitude(-122.4194) + ->setRadius(10.5) + ->setUnitType('mi'); + $expected = [ + 'location' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 37.7749, + 'longitude' => -122.4194, + 'radius' => 10.5, + 'unitType' => 'mi', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } + + public function testConstructorArgumentsAssociativeArray(): void + { + $args = [ + 'location', + [ + 'latitude' => 40.7128, + 'longitude' => -74.0060, + 'radius' => 5, + 'unitType' => 'km', + ], + ]; + $operator = new InRadiusFromCoords($args); + $expected = [ + 'location' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 40.7128, + 'longitude' => -74.0060, + 'radius' => 5.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $operator->setField('location')->compile()); + } + + public function testConstructorArgumentsIndexedArray(): void + { + $args = ['geo', 12.34, 56.78, 25, 'mi']; + $operator = new InRadiusFromCoords($args); + $expected = [ + 'geo' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 12.34, + 'longitude' => 56.78, + 'radius' => 25.0, + 'unitType' => 'mi', + ], + ], + ]; + $this->assertEquals($expected, $operator->setField('geo')->compile()); + } + + public function testSetValueWithIndexedArray(): void + { + $operator = new InRadiusFromCoords(); + $operator->setField('geo') + ->setValue([51.5074, -0.1278, 15, 'km']); + $expected = [ + 'geo' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 51.5074, + 'longitude' => -0.1278, + 'radius' => 15.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } + + public function testSetValueWithCoordinatesArrayAsFirstElement(): void + { + $operator = new InRadiusFromCoords(); + $operator->setField('geo') + ->setValue([[33.33, 44.44], 100, 'km']); + $expected = [ + 'geo' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 33.33, + 'longitude' => 44.44, + 'radius' => 100.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } + + public function testDefaultsWhenMissingValues(): void + { + $operator = new InRadiusFromCoords(); + $operator->setField('geo')->setValue([]); + $expected = [ + 'geo' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 0.0, + 'longitude' => 0.0, + 'radius' => 0.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } + + public function testSetCoordinatesWithAssociativeArray(): void + { + $operator = new InRadiusFromCoords(); + $operator->setField('geo')->setCoordinates([ + 'latitude' => 10.1, + 'longitude' => 20.2, + ])->setRadius(7)->setUnitType('mi'); + $expected = [ + 'geo' => [ + InRadiusFromCoords::OPERATOR => [ + 'latitude' => 10.1, + 'longitude' => 20.2, + 'radius' => 7.0, + 'unitType' => 'mi', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } +} diff --git a/tests/Endpoint/Data/Filters/Operator/InRadiusFromZipTest.php b/tests/Endpoint/Data/Filters/Operator/InRadiusFromZipTest.php new file mode 100644 index 0000000..8c95248 --- /dev/null +++ b/tests/Endpoint/Data/Filters/Operator/InRadiusFromZipTest.php @@ -0,0 +1,145 @@ +setField('address') + ->setZipCode('90210') + ->setCountry('US') + ->setRadius(25) + ->setUnitType('mi'); + $expected = [ + 'address' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => '90210', + 'countries' => 'US', + 'radius' => 25.0, + 'unitType' => 'mi', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } + + public function testConstructorArgumentsIndexedArray(): void + { + $args = ['address', '12345', 'CA', 50, 'km']; + $operator = new InRadiusFromZip($args); + $expected = [ + 'address' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => '12345', + 'countries' => 'CA', + 'radius' => 50.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $operator->setField('address')->compile()); + } + + public function testConstructorArgumentsAssociativeArray(): void + { + $args = [ + 'address', + [ + 'zipCode' => '54321', + 'countries' => 'MX', + 'radius' => 10, + 'unitType' => 'mi', + ], + ]; + $operator = new InRadiusFromZip($args); + $expected = [ + 'address' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => '54321', + 'countries' => 'MX', + 'radius' => 10.0, + 'unitType' => 'mi', + ], + ], + ]; + $this->assertEquals($expected, $operator->setField('address')->compile()); + } + + public function testSetValueWithIndexedArray(): void + { + $operator = new InRadiusFromZip(); + $operator->setField('loc')->setValue(['11111', 'US', 5, 'km']); + $expected = [ + 'loc' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => '11111', + 'countries' => 'US', + 'radius' => 5.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } + + public function testSetValueWithAssociativeArray(): void + { + $operator = new InRadiusFromZip(); + $operator->setField('loc')->setValue([ + 'zipCode' => '22222', + 'countries' => 'CA', + 'radius' => 15, + 'unitType' => 'mi', + ]); + $expected = [ + 'loc' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => '22222', + 'countries' => 'CA', + 'radius' => 15.0, + 'unitType' => 'mi', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } + + public function testSetValueWithScalarZipCode(): void + { + $operator = new InRadiusFromZip(); + $operator->setField('loc')->setValue('33333'); + $expected = [ + 'loc' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => '33333', + 'countries' => 'US', // default + 'radius' => 0.0, // default + 'unitType' => 'km', // default + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } + + public function testDefaultsWhenMissingValues(): void + { + $operator = new InRadiusFromZip(); + $operator->setField('loc')->setValue([]); + $expected = [ + 'loc' => [ + InRadiusFromZip::OPERATOR => [ + 'zipCode' => 0, + 'countries' => 'US', + 'radius' => 0.0, + 'unitType' => 'km', + ], + ], + ]; + $this->assertEquals($expected, $operator->compile()); + } +} diff --git a/tests/Endpoint/IntegrateTest.php b/tests/Endpoint/IntegrateTest.php index 245b34e..8b032b4 100644 --- a/tests/Endpoint/IntegrateTest.php +++ b/tests/Endpoint/IntegrateTest.php @@ -99,6 +99,7 @@ public function testUpsert(): void $this->assertEquals('test', $endpoint->foobar_c); $this->assertEquals('Prospect', $endpoint['account_type']); } + public function testUpsertWithSetField(): void { $this->client->mockResponses->append(new Response('201'), new Response('201')); @@ -114,6 +115,7 @@ public function testUpsertWithSetField(): void $endpoint->setFields(['foobar', 'bar']); $endpoint->upsert(); + $request = $this->client->mockResponses->getLastRequest(); $body = json_decode($request->getBody()->getContents(), true); $this->assertArrayHasKey('fields', $body);