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/Client/SugarApi.php b/src/Client/SugarApi.php index 27e6f5c..1145714 100644 --- a/src/Client/SugarApi.php +++ b/src/Client/SugarApi.php @@ -14,6 +14,7 @@ use Sugarcrm\REST\Endpoint\MLPackage; use Sugarcrm\REST\Endpoint\ModuleLoader; use Sugarcrm\REST\Endpoint\Ping; +use Sugarcrm\REST\Endpoint\Rest; use Sugarcrm\REST\Endpoint\Smart; use Sugarcrm\REST\Endpoint\SugarBean; use Sugarcrm\REST\Endpoint\ModuleFilter; @@ -48,6 +49,7 @@ * @method ModuleLoader moduleLoader() - * @method MLPackage mlp(string $id = null) * @method Integrate integrate(string $module = '', string $id = '') + * @method Rest rest(string $endpoint = '') */ class SugarApi extends AbstractClient implements PlatformAwareInterface { diff --git a/src/Endpoint/Abstracts/AbstractSmartSugarEndpoint.php b/src/Endpoint/Abstracts/AbstractSmartSugarEndpoint.php index 76d46cb..e291ffb 100644 --- a/src/Endpoint/Abstracts/AbstractSmartSugarEndpoint.php +++ b/src/Endpoint/Abstracts/AbstractSmartSugarEndpoint.php @@ -6,11 +6,13 @@ namespace Sugarcrm\REST\Endpoint\Abstracts; +use GuzzleHttp\Psr7\Request; use MRussell\REST\Endpoint\Data\ValidatedEndpointData; use MRussell\REST\Endpoint\SmartEndpoint; use MRussell\REST\Traits\PsrLoggerTrait; use Sugarcrm\REST\Endpoint\SugarEndpointInterface; use Sugarcrm\REST\Endpoint\Traits\CompileRequestTrait; +use Sugarcrm\REST\Endpoint\Traits\CustomHeadersTrait; /** * Provide a smarter interface for Endpoints, to better manage passed in data @@ -20,6 +22,12 @@ abstract class AbstractSmartSugarEndpoint extends SmartEndpoint implements Sugar { use CompileRequestTrait; use PsrLoggerTrait; + use CustomHeadersTrait; protected string $_dataInterface = ValidatedEndpointData::class; + + protected function configureRequest(Request $request, $data): Request + { + return parent::configureRequest($this->addCustomHeadersToRequest($request), $data); + } } diff --git a/src/Endpoint/Abstracts/AbstractSugarBeanCollectionEndpoint.php b/src/Endpoint/Abstracts/AbstractSugarBeanCollectionEndpoint.php index 4fdf5b9..4387cdf 100644 --- a/src/Endpoint/Abstracts/AbstractSugarBeanCollectionEndpoint.php +++ b/src/Endpoint/Abstracts/AbstractSugarBeanCollectionEndpoint.php @@ -6,8 +6,10 @@ namespace Sugarcrm\REST\Endpoint\Abstracts; +use GuzzleHttp\Psr7\Request; use Sugarcrm\REST\Endpoint\SugarBean; use MRussell\REST\Endpoint\Abstracts\AbstractModelEndpoint; +use Sugarcrm\REST\Endpoint\Traits\CustomHeadersTrait; use Sugarcrm\REST\Endpoint\Traits\FieldsDataTrait; use Sugarcrm\REST\Endpoint\Traits\ModuleAwareTrait; @@ -22,6 +24,7 @@ abstract class AbstractSugarBeanCollectionEndpoint extends AbstractSugarCollecti { use FieldsDataTrait; use ModuleAwareTrait; + use CustomHeadersTrait; public const SUGAR_ORDERBY_DATA_PROPERTY = 'order_by'; @@ -97,6 +100,11 @@ protected function configurePayload(): mixed return $this->configureFieldsDataProps($data); } + protected function configureRequest(Request $request, $data): Request + { + return parent::configureRequest($this->addCustomHeadersToRequest($request), $data); + } + /** * Add module to url options * @inheritdoc diff --git a/src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php b/src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php index 6557f69..957610b 100644 --- a/src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php +++ b/src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php @@ -22,6 +22,7 @@ use Sugarcrm\REST\Endpoint\Integrate; use Sugarcrm\REST\Endpoint\SugarEndpointInterface; use Sugarcrm\REST\Endpoint\Traits\CompileRequestTrait; +use Sugarcrm\REST\Endpoint\Traits\CustomHeadersTrait; use Sugarcrm\REST\Endpoint\Traits\FieldsDataTrait; use Sugarcrm\REST\Endpoint\Traits\IntegrateSyncKeyTrait; use Sugarcrm\REST\Endpoint\Traits\ModuleAwareTrait; @@ -53,6 +54,7 @@ abstract class AbstractSugarBeanEndpoint extends ModelEndpoint implements SugarE use FieldsDataTrait; use FileUploadsTrait; use IntegrateSyncKeyTrait; + use CustomHeadersTrait; public const MODEL_ACTION_VAR = 'action'; @@ -173,7 +175,7 @@ protected function configureRequest(Request $request, $data): Request $data = $this->configureFieldsDataProps($data); } - return parent::configureRequest($request, $data); + return parent::configureRequest($this->addCustomHeadersToRequest($request), $data); } /** diff --git a/src/Endpoint/Abstracts/AbstractSugarCollectionEndpoint.php b/src/Endpoint/Abstracts/AbstractSugarCollectionEndpoint.php index 810f363..6dbdff5 100644 --- a/src/Endpoint/Abstracts/AbstractSugarCollectionEndpoint.php +++ b/src/Endpoint/Abstracts/AbstractSugarCollectionEndpoint.php @@ -6,6 +6,7 @@ namespace Sugarcrm\REST\Endpoint\Abstracts; +use GuzzleHttp\Psr7\Request; use MRussell\REST\Exception\Endpoint\InvalidRequest; use GuzzleHttp\Psr7\Response; use MRussell\REST\Endpoint\Data\AbstractEndpointData; @@ -13,6 +14,7 @@ use MRussell\REST\Traits\PsrLoggerTrait; use Sugarcrm\REST\Endpoint\SugarEndpointInterface; use Sugarcrm\REST\Endpoint\Traits\CompileRequestTrait; +use Sugarcrm\REST\Endpoint\Traits\CustomHeadersTrait; /** * Provides access to a multi-bean collection retrieved from Sugar 7 REST Api @@ -23,6 +25,7 @@ abstract class AbstractSugarCollectionEndpoint extends CollectionEndpoint implem { use CompileRequestTrait; use PsrLoggerTrait; + use CustomHeadersTrait; public const SUGAR_OFFSET_PROPERTY = 'offset'; @@ -82,6 +85,11 @@ protected function configurePayload(): mixed return $data; } + protected function configureRequest(Request $request, $data): Request + { + return parent::configureRequest($this->addCustomHeadersToRequest($request), $data); + } + /** * Get the configured offset */ diff --git a/src/Endpoint/Abstracts/AbstractSugarEndpoint.php b/src/Endpoint/Abstracts/AbstractSugarEndpoint.php index bd147cc..489a204 100644 --- a/src/Endpoint/Abstracts/AbstractSugarEndpoint.php +++ b/src/Endpoint/Abstracts/AbstractSugarEndpoint.php @@ -6,10 +6,12 @@ namespace Sugarcrm\REST\Endpoint\Abstracts; +use GuzzleHttp\Psr7\Request; use MRussell\REST\Endpoint\Endpoint; use MRussell\REST\Traits\PsrLoggerTrait; use Sugarcrm\REST\Endpoint\SugarEndpointInterface; use Sugarcrm\REST\Endpoint\Traits\CompileRequestTrait; +use Sugarcrm\REST\Endpoint\Traits\CustomHeadersTrait; /** * Base Sugar API Endpoint for the simplest of REST functionality @@ -19,4 +21,10 @@ abstract class AbstractSugarEndpoint extends Endpoint implements SugarEndpointIn { use CompileRequestTrait; use PsrLoggerTrait; + use CustomHeadersTrait; + + protected function configureRequest(Request $request, $data): Request + { + return parent::configureRequest($this->addCustomHeadersToRequest($request), $data); + } } 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 @@ + Integrate::class, self::ENDPOINT_PROPERTIES => [], ], + [ + self::ENDPOINT_NAME => 'rest', + self::ENDPOINT_CLASS => Rest::class, + self::ENDPOINT_PROPERTIES => [], + ], ]; } diff --git a/src/Endpoint/Rest.php b/src/Endpoint/Rest.php new file mode 100644 index 0000000..ccf10f3 --- /dev/null +++ b/src/Endpoint/Rest.php @@ -0,0 +1,74 @@ +rest('custom/endpoint')->get(); + * $client->rest('custom/endpoint')->post($data); + * $client->rest('custom/endpoint')->put($data); + * $client->rest('custom/endpoint')->delete(); + * $client->rest('custom/endpoint')->patch($data); + * $client->rest('custom/endpoint')->withHeaders($headers)->get(); + * $client->rest('Contacts')->setData(['fields' => 'id,first_name,last_name', 'max_num' => 1])->get(); + * etc. + */ +class Rest extends Generic +{ + protected static array $_DEFAULT_PROPERTIES = [ + self::PROPERTY_URL => '$endpoint', + self::PROPERTY_AUTH => true, + self::PROPERTY_HTTP_METHOD => "GET", + ]; + + public function get(mixed $data = null): static + { + $this->setProperty(self::PROPERTY_HTTP_METHOD, 'GET'); + if (!is_null($data)) { + $this->setData($data); + } + return $this->execute(); + } + + public function post(mixed $data = null): static + { + $this->setProperty(self::PROPERTY_HTTP_METHOD, 'POST'); + if (!is_null($data)) { + $this->setData($data); + } + return $this->execute(); + } + + public function put(mixed $data = null): static + { + $this->setProperty(self::PROPERTY_HTTP_METHOD, 'PUT'); + if (!is_null($data)) { + $this->setData($data); + } + return $this->execute(); + } + + public function delete(): static + { + $this->setProperty(self::PROPERTY_HTTP_METHOD, 'DELETE'); + return $this->execute(); + } + + public function patch(mixed $data = null): static + { + $this->setProperty(self::PROPERTY_HTTP_METHOD, 'PATCH'); + if (!is_null($data)) { + $this->setData($data); + } + return $this->execute(); + } + +} diff --git a/src/Endpoint/Traits/CustomHeadersTrait.php b/src/Endpoint/Traits/CustomHeadersTrait.php new file mode 100644 index 0000000..fac6a7c --- /dev/null +++ b/src/Endpoint/Traits/CustomHeadersTrait.php @@ -0,0 +1,47 @@ +customHeaders[$normalized] = [$name, $value]; + return $this; + } + + /** + * Remove a custom header from the request + * @param string $name + * @return $this + */ + public function removeCustomHeader(string $name): static + { + $normalized = strtolower($name); + if (isset($this->customHeaders[$normalized])) { + unset($this->customHeaders[$normalized]); + } + return $this; + } + + protected function addCustomHeadersToRequest(Request $request): Request + { + if (!empty($this->customHeaders)) { + foreach ($this->customHeaders as $headerNormalized => $values) { + $request = $request->withHeader($values[0], $values[1]); + } + } + return $request; + } +} diff --git a/src/Exception/Filter/MissingFieldForDateExpression.php b/src/Exception/Filter/MissingFieldForFilterExpression.php similarity index 65% rename from src/Exception/Filter/MissingFieldForDateExpression.php rename to src/Exception/Filter/MissingFieldForFilterExpression.php index 13dc214..0422e4e 100644 --- a/src/Exception/Filter/MissingFieldForDateExpression.php +++ b/src/Exception/Filter/MissingFieldForFilterExpression.php @@ -11,7 +11,7 @@ /** * @package Sugarcrm\REST\Exception\Filter */ -class MissingFieldForDateExpression extends EndpointException +class MissingFieldForFilterExpression extends EndpointException { - protected $message = 'Field not configured on DateExpression'; + protected $message = 'Field not configured on Filter Expression'; } diff --git a/tests/Endpoint/Data/Filters/AbstractOperatorTest.php b/tests/Endpoint/Data/Filters/AbstractOperatorTest.php index 3beeb0e..bcb3363 100644 --- a/tests/Endpoint/Data/Filters/AbstractOperatorTest.php +++ b/tests/Endpoint/Data/Filters/AbstractOperatorTest.php @@ -57,8 +57,6 @@ protected function tearDown(): void public function testConstructor(): void { $Operator = new Contains(); - $this->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); diff --git a/tests/Endpoint/RestTest.php b/tests/Endpoint/RestTest.php new file mode 100644 index 0000000..4dac673 --- /dev/null +++ b/tests/Endpoint/RestTest.php @@ -0,0 +1,125 @@ +client = new Client(); + parent::setUp(); + } + + public function testGetRequestToCustomEndpoint() + { + $this->client->mockResponses->append(new Response(200, [], 'OK')); + $rest = $this->client->rest('custom/endpoint'); + $rest->setBaseUrl('http://localhost/rest/v11'); + $rest->get(['foo' => 'bar']); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http://localhost/rest/v11/custom/endpoint?foo=bar', (string) $request->getUri()); + $this->assertStringContainsString('foo=bar', (string) $request->getUri()->getQuery()); + } + + public function testPostRequestWithData() + { + $this->client->mockResponses->append(new Response(200, [], 'OK')); + $rest = new Rest([], ['custom/endpoint']); + $rest->setClient($this->client); + $rest->setBaseUrl('http://localhost/rest/v11'); + $data = ['foo' => 'bar', 'baz' => 'qux']; + $rest->post($data); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('http://localhost/rest/v11/custom/endpoint', (string) $request->getUri()); + $body = json_decode($request->getBody()->getContents(), true); + $this->assertEquals($data, $body); + } + + public function testPutRequestWithData() + { + $this->client->mockResponses->append(new Response(200, [], 'OK')); + $rest = new Rest([], ['custom/endpoint']); + $rest->setClient($this->client); + $rest->setBaseUrl('http://localhost/rest/v11'); + $data = ['foo' => 'bar']; + $rest->put($data); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('PUT', $request->getMethod()); + $body = json_decode($request->getBody()->getContents(), true); + $this->assertEquals($data, $body); + } + + public function testPatchRequestWithData() + { + $this->client->mockResponses->append(new Response(200, [], 'OK')); + $rest = new Rest([], ['custom/endpoint']); + $rest->setClient($this->client); + $rest->setBaseUrl('http://localhost/rest/v11'); + $data = ['foo' => 'bar']; + $rest->patch($data); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('PATCH', $request->getMethod()); + $body = json_decode($request->getBody()->getContents(), true); + $this->assertEquals($data, $body); + } + + public function testDeleteRequest() + { + $this->client->mockResponses->append(new Response(200, [], 'OK')); + $rest = $this->client->rest('custom/endpoint'); + $rest->setBaseUrl('http://localhost/rest/v11'); + $rest->delete(); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('DELETE', $request->getMethod()); + $this->assertEquals('http://localhost/rest/v11/custom/endpoint', (string) $request->getUri()); + } + + public function testAddCustomHeader() + { + $this->client->mockResponses->append(new Response(200, [], 'OK')); + $rest = $this->client->rest('custom/endpoint'); + $rest->setBaseUrl('http://localhost/rest/v11'); + $rest->addCustomHeader('X-Test-Header', 'value1'); + $rest->get(); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertTrue($request->hasHeader('X-Test-Header')); + $this->assertEquals(['value1'], $request->getHeader('X-Test-Header')); + } + + public function testRemoveCustomHeader() + { + $this->client->mockResponses->append(new Response(200, [], 'OK')); + $rest = $this->client->rest('custom/endpoint'); + $rest->setBaseUrl('http://localhost/rest/v11'); + $rest->addCustomHeader('X-Test-Header', 'value1'); + $rest->removeCustomHeader('X-Test-Header'); + $rest->get(); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertFalse($request->hasHeader('X-Test-Header')); + } + + public function testMultipleCustomHeaders() + { + $this->client->mockResponses->append(new Response(200, [], 'OK')); + $rest = $this->client->rest('custom/endpoint'); + $rest->setBaseUrl('http://localhost/rest/v11'); + $rest->addCustomHeader('X-Test-Header', 'value1'); + $rest->addCustomHeader('Another-Header', 'value2'); + $rest->get(); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertTrue($request->hasHeader('X-Test-Header')); + $this->assertEquals(['value1'], $request->getHeader('X-Test-Header')); + $this->assertTrue($request->hasHeader('Another-Header')); + $this->assertEquals(['value2'], $request->getHeader('Another-Header')); + } +}