diff --git a/CHANGELOG.md b/CHANGELOG.md index e88e01d3..e20ac9ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). -## Unreleased +## [2.1.0](https://github.com/packlink-dev/ecommerce_module_core/compare/v2.0.12...v2.1.0) +** BREAKING CHANGES ** + +### Added +- Added a socket http client for async requests +- Added task execution priority +- Added async batch starter +- Added task runner keep alive mechanism +- Added batch task cleanup task + +### Changed +- Changed when the schedules are created +- BaseDto is deprecated ## [2.0.12](https://github.com/packlink-dev/ecommerce_module_core/compare/v2.0.11...v2.0.12) - 2020-05-25 ### Changed diff --git a/src/BusinessLogic/Configuration.php b/src/BusinessLogic/Configuration.php index 35f94ddd..f1cf1e0e 100644 --- a/src/BusinessLogic/Configuration.php +++ b/src/BusinessLogic/Configuration.php @@ -22,6 +22,10 @@ abstract class Configuration extends \Logeecom\Infrastructure\Configuration\Conf * Default scheduler queue name. */ const DEFAULT_SCHEDULER_QUEUE_NAME = 'SchedulerCheckTaskQueue'; + /** + * Default task retention time expressed in days. After this time tasks are not necesseary any more in the system. + */ + const DEFAULT_MAX_TASK_AGE = 7; /** * Singleton instance of this class. * @@ -29,6 +33,53 @@ abstract class Configuration extends \Logeecom\Infrastructure\Configuration\Conf */ protected static $instance; + /** + * Retrieves max task age in days. Tasks older than the given number of days are no longer needed in the system. + * + * @return int Max task age in days. + */ + public function getMaxTaskAge() + { + return $this->getConfigValue('maxTaskAge', self::DEFAULT_MAX_TASK_AGE); + } + + /** + * Sets max task age. + * + * @param int $maxAge Positive integer. Denotes max task age in days. + */ + public function setMaxTaskAge($maxAge) + { + $this->saveConfigValue('maxTaskAge', $maxAge); + } + + /** + * Checks if any shipment has been created on Packlink. + * + * @return bool True if a shipment has been created; FALSE otherwise. + */ + public function isFirstShipmentDraftCreated() + { + $value = $this->getConfigValue('isFirstShipmentDraftCreated'); + + // If the value is null, that implies that the user has been registered + // before tracking of this flag has been implemented. + // For such users we will return true, + // since this flag is used to enqueue schedules if its value is false, and already registered + // users have schedules since the schedules are created when they've registered in the app. + return ($value || $value === null); + } + + /** + * Sets the flag that indicates that the first shipment has been created to true. + * + * @param bool $status + */ + public function setFirstShipmentDraftCreated($status = true) + { + $this->saveConfigValue('isFirstShipmentDraftCreated', $status); + } + /** * Returns web-hook callback URL for current system. * @@ -243,4 +294,20 @@ public function isSetupFinished() { return $this->getConfigValue('setupFinished') ?: false; } + + /** + * Determines whether the configuration entry is system specific. + * + * @param string $name Configuration entry name. + * + * @return bool + */ + protected function isSystemSpecific($name) + { + if ($name === 'maxTaskAge') { + return false; + } + + return parent::isSystemSpecific($name); + } } diff --git a/src/BusinessLogic/Controllers/AutoConfigurationController.php b/src/BusinessLogic/Controllers/AutoConfigurationController.php index a1ee77ae..67a246e8 100644 --- a/src/BusinessLogic/Controllers/AutoConfigurationController.php +++ b/src/BusinessLogic/Controllers/AutoConfigurationController.php @@ -78,6 +78,12 @@ protected function enqueueUpdateServicesTask(Configuration $configService) // enqueue the task for updating shipping services /** @var QueueService $queueService */ $queueService = ServiceRegister::getService(QueueService::CLASS_NAME); - $queueService->enqueue($configService->getDefaultQueueName(), new UpdateShippingServicesTask()); + $task = new UpdateShippingServicesTask(); + $queueService->enqueue( + $configService->getDefaultQueueName(), + $task, + $configService->getContext(), + $task->getPriority() + ); } } diff --git a/src/BusinessLogic/Controllers/DTO/ShippingMethodConfiguration.php b/src/BusinessLogic/Controllers/DTO/ShippingMethodConfiguration.php index cf87756e..9b69d983 100644 --- a/src/BusinessLogic/Controllers/DTO/ShippingMethodConfiguration.php +++ b/src/BusinessLogic/Controllers/DTO/ShippingMethodConfiguration.php @@ -2,7 +2,7 @@ namespace Packlink\BusinessLogic\Controllers\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; use Packlink\BusinessLogic\ShippingMethod\Models\FixedPricePolicy; use Packlink\BusinessLogic\ShippingMethod\Models\PercentPricePolicy; use Packlink\BusinessLogic\ShippingMethod\Models\ShippingMethod; @@ -12,7 +12,7 @@ * * @package Packlink\BusinessLogic\Controllers\DTO */ -class ShippingMethodConfiguration extends BaseDto +class ShippingMethodConfiguration extends DataTransferObject { /** * Shipping method identifier. diff --git a/src/BusinessLogic/Country/Country.php b/src/BusinessLogic/Country/Country.php index 1856ff3a..1cd74f81 100644 --- a/src/BusinessLogic/Country/Country.php +++ b/src/BusinessLogic/Country/Country.php @@ -87,9 +87,9 @@ public static function fromArray(array $raw) /** @var static $instance */ $instance = parent::fromArray($raw); - $instance->postalCode = static::getValue($raw, 'postal_code'); - $instance->registrationLink = static::getValue($raw, 'registration_link'); - $instance->platformCountry = static::getValue($raw, 'platform_country'); + $instance->postalCode = static::getDataValue($raw, 'postal_code'); + $instance->registrationLink = static::getDataValue($raw, 'registration_link'); + $instance->platformCountry = static::getDataValue($raw, 'platform_country'); return $instance; } diff --git a/src/BusinessLogic/DTO/BaseDto.php b/src/BusinessLogic/DTO/BaseDto.php index f98b165e..857fb5cc 100644 --- a/src/BusinessLogic/DTO/BaseDto.php +++ b/src/BusinessLogic/DTO/BaseDto.php @@ -6,6 +6,9 @@ * Base class for all data transfer objects. * * @package Packlink\BusinessLogic\Http\DTO + * + * @deprecated This class is replaced with DataTransferObject from infrastructure core. It is left behind for backwards + * compatibility. */ abstract class BaseDto { diff --git a/src/BusinessLogic/DTO/FrontDto.php b/src/BusinessLogic/DTO/FrontDto.php index fd3ada36..9e5f238e 100644 --- a/src/BusinessLogic/DTO/FrontDto.php +++ b/src/BusinessLogic/DTO/FrontDto.php @@ -2,6 +2,7 @@ namespace Packlink\BusinessLogic\DTO; +use Logeecom\Infrastructure\Data\DataTransferObject; use Packlink\BusinessLogic\DTO\Exceptions\FrontDtoValidationException; /** @@ -9,7 +10,7 @@ * * @package Packlink\BusinessLogic\DTO */ -abstract class FrontDto extends BaseDto +abstract class FrontDto extends DataTransferObject { /** * Fully qualified name of this class. diff --git a/src/BusinessLogic/DTO/FrontDtoFactory.php b/src/BusinessLogic/DTO/FrontDtoFactory.php index 461ef442..fb43cc8a 100644 --- a/src/BusinessLogic/DTO/FrontDtoFactory.php +++ b/src/BusinessLogic/DTO/FrontDtoFactory.php @@ -86,7 +86,7 @@ public static function getFromBatch($key, array $payload) /** @var FrontDto $className Actually, it is a string, but this is for code completion purpose. */ $className = self::$registry[$key]; - return $className::fromArrayBatch($payload); + return $className::fromBatch($payload); } /** diff --git a/src/BusinessLogic/Http/DTO/Analytics.php b/src/BusinessLogic/Http/DTO/Analytics.php index 8a08a618..f049ca9f 100644 --- a/src/BusinessLogic/Http/DTO/Analytics.php +++ b/src/BusinessLogic/Http/DTO/Analytics.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class Analytics. * * @package Packlink\BusinessLogic\Http\DTO */ -class Analytics extends BaseDto +class Analytics extends DataTransferObject { const EVENT_SETUP = 'setup'; const EVENT_CONFIGURATION = 'api_configuration'; diff --git a/src/BusinessLogic/Http/DTO/Draft.php b/src/BusinessLogic/Http/DTO/Draft.php index 562753ae..c2593af7 100644 --- a/src/BusinessLogic/Http/DTO/Draft.php +++ b/src/BusinessLogic/Http/DTO/Draft.php @@ -2,7 +2,7 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; use Packlink\BusinessLogic\Http\DTO\Draft\AdditionalData; use Packlink\BusinessLogic\Http\DTO\Draft\Address; use Packlink\BusinessLogic\Http\DTO\Draft\DraftPrice; @@ -12,7 +12,7 @@ * * @package Packlink\BusinessLogic\Http\DTO */ -class Draft extends BaseDto +class Draft extends DataTransferObject { /** * Unique user identifier. diff --git a/src/BusinessLogic/Http/DTO/Draft/AdditionalData.php b/src/BusinessLogic/Http/DTO/Draft/AdditionalData.php index 9c2ca14e..ff2d590d 100644 --- a/src/BusinessLogic/Http/DTO/Draft/AdditionalData.php +++ b/src/BusinessLogic/Http/DTO/Draft/AdditionalData.php @@ -2,13 +2,13 @@ namespace Packlink\BusinessLogic\Http\DTO\Draft; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class AdditionalData * @package Packlink\BusinessLogic\Http\DTO\Draft */ -class AdditionalData extends BaseDto +class AdditionalData extends DataTransferObject { /** * Value of the postal zone id corresponding to the origin postal code. diff --git a/src/BusinessLogic/Http/DTO/Draft/Address.php b/src/BusinessLogic/Http/DTO/Draft/Address.php index fb80e01c..3794ccd1 100644 --- a/src/BusinessLogic/Http/DTO/Draft/Address.php +++ b/src/BusinessLogic/Http/DTO/Draft/Address.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO\Draft; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class Address. * * @package Packlink\BusinessLogic\Http\DTO\Draft */ -class Address extends BaseDto +class Address extends DataTransferObject { /** * Name of sender/receiver. diff --git a/src/BusinessLogic/Http/DTO/Draft/DraftItem.php b/src/BusinessLogic/Http/DTO/Draft/DraftItem.php index 3ae7bd07..d86dbba5 100644 --- a/src/BusinessLogic/Http/DTO/Draft/DraftItem.php +++ b/src/BusinessLogic/Http/DTO/Draft/DraftItem.php @@ -2,13 +2,13 @@ namespace Packlink\BusinessLogic\Http\DTO\Draft; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class DraftItem * @package Packlink\BusinessLogic\Http\DTO\Draft */ -class DraftItem extends BaseDto +class DraftItem extends DataTransferObject { /** * Item price. diff --git a/src/BusinessLogic/Http/DTO/Draft/DraftPrice.php b/src/BusinessLogic/Http/DTO/Draft/DraftPrice.php index 8b09c03c..569e6798 100644 --- a/src/BusinessLogic/Http/DTO/Draft/DraftPrice.php +++ b/src/BusinessLogic/Http/DTO/Draft/DraftPrice.php @@ -2,13 +2,13 @@ namespace Packlink\BusinessLogic\Http\DTO\Draft; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class DraftPrice * @package Packlink\BusinessLogic\Http\DTO\Draft */ -class DraftPrice extends BaseDto +class DraftPrice extends DataTransferObject { /** * Value of item in EUR without taxes. @@ -34,6 +34,7 @@ class DraftPrice extends BaseDto * @var ItemPrice[] */ public $items = array(); + /** * Transforms DTO to its array format suitable for http client. * diff --git a/src/BusinessLogic/Http/DTO/Draft/ItemPrice.php b/src/BusinessLogic/Http/DTO/Draft/ItemPrice.php index 95aaa0f6..6a4f09e7 100644 --- a/src/BusinessLogic/Http/DTO/Draft/ItemPrice.php +++ b/src/BusinessLogic/Http/DTO/Draft/ItemPrice.php @@ -2,13 +2,13 @@ namespace Packlink\BusinessLogic\Http\DTO\Draft; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class ItemPrice * @package Packlink\BusinessLogic\Http\DTO\Draft */ -class ItemPrice extends BaseDto +class ItemPrice extends DataTransferObject { /** * Value of item in EUR without taxes. diff --git a/src/BusinessLogic/Http/DTO/DropOff.php b/src/BusinessLogic/Http/DTO/DropOff.php index cc2191d4..81800623 100644 --- a/src/BusinessLogic/Http/DTO/DropOff.php +++ b/src/BusinessLogic/Http/DTO/DropOff.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class DropOff. * * @package Packlink\BusinessLogic\Http\DTO */ -class DropOff extends BaseDto +class DropOff extends DataTransferObject { /** * Unique identifier of drop-off point. @@ -105,7 +105,7 @@ public function toArray() 'lat' => $this->lat, 'long' => $this->long, 'phone' => $this->phone, - 'workingHours' => $this->workingHours + 'workingHours' => $this->workingHours, ); } @@ -120,17 +120,17 @@ public static function fromArray(array $raw) { $entity = new self(); - $entity->id = static::getValue($raw, 'id'); - $entity->name = static::getValue($raw, 'commerce_name'); - $entity->type = static::getValue($raw, 'type'); - $entity->countryCode = static::getValue($raw, 'country'); - $entity->state = static::getValue($raw, 'state'); - $entity->zip = static::getValue($raw, 'zip'); - $entity->city = static::getValue($raw, 'city'); - $entity->address = static::getValue($raw, 'address'); - $entity->lat = static::getValue($raw, 'lat', 0); - $entity->long = static::getValue($raw, 'long', 0); - $entity->phone = static::getValue($raw, 'phone'); + $entity->id = static::getDataValue($raw, 'id'); + $entity->name = static::getDataValue($raw, 'commerce_name'); + $entity->type = static::getDataValue($raw, 'type'); + $entity->countryCode = static::getDataValue($raw, 'country'); + $entity->state = static::getDataValue($raw, 'state'); + $entity->zip = static::getDataValue($raw, 'zip'); + $entity->city = static::getDataValue($raw, 'city'); + $entity->address = static::getDataValue($raw, 'address'); + $entity->lat = static::getDataValue($raw, 'lat', 0); + $entity->long = static::getDataValue($raw, 'long', 0); + $entity->phone = static::getDataValue($raw, 'phone'); $entity->workingHours = !empty($raw['opening_times']['opening_times']) ? $raw['opening_times']['opening_times'] : array(); diff --git a/src/BusinessLogic/Http/DTO/LocationInfo.php b/src/BusinessLogic/Http/DTO/LocationInfo.php index 74bc6e42..bf762dd8 100644 --- a/src/BusinessLogic/Http/DTO/LocationInfo.php +++ b/src/BusinessLogic/Http/DTO/LocationInfo.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class LocationInfo. * * @package Packlink\BusinessLogic\Http\DTO */ -class LocationInfo extends BaseDto +class LocationInfo extends DataTransferObject { /** * Id. @@ -53,11 +53,11 @@ public static function fromArray(array $raw) { $result = new self(); - $result->id = static::getValue($raw, 'id'); - $result->state = static::getValue($raw, 'state'); - $result->city = static::getValue($raw, 'city'); - $result->zipcode = static::getValue($raw, 'zipcode'); - $result->text = static::getValue($raw, 'text'); + $result->id = static::getDataValue($raw, 'id'); + $result->state = static::getDataValue($raw, 'state'); + $result->city = static::getDataValue($raw, 'city'); + $result->zipcode = static::getDataValue($raw, 'zipcode'); + $result->text = static::getDataValue($raw, 'text'); return $result; } diff --git a/src/BusinessLogic/Http/DTO/Package.php b/src/BusinessLogic/Http/DTO/Package.php index 6112db5a..6f06bebd 100644 --- a/src/BusinessLogic/Http/DTO/Package.php +++ b/src/BusinessLogic/Http/DTO/Package.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class Package. * * @package Packlink\BusinessLogic\Http\DTO */ -class Package extends BaseDto +class Package extends DataTransferObject { /** * Weight of package in kg. @@ -72,10 +72,10 @@ public static function defaultPackage() public static function fromArray(array $raw) { $instance = new static(); - $instance->weight = static::getValue($raw, 'weight', 0.0); - $instance->length = static::getValue($raw, 'length', 0.0); - $instance->height = static::getValue($raw, 'height', 0.0); - $instance->width = static::getValue($raw, 'width', 0.0); + $instance->weight = static::getDataValue($raw, 'weight', 0.0); + $instance->length = static::getDataValue($raw, 'length', 0.0); + $instance->height = static::getDataValue($raw, 'height', 0.0); + $instance->width = static::getDataValue($raw, 'width', 0.0); return $instance; } diff --git a/src/BusinessLogic/Http/DTO/PostalCode.php b/src/BusinessLogic/Http/DTO/PostalCode.php index a60990cb..2a4bb9ff 100644 --- a/src/BusinessLogic/Http/DTO/PostalCode.php +++ b/src/BusinessLogic/Http/DTO/PostalCode.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class PostalCode. * * @package Packlink\BusinessLogic\Http\DTO */ -class PostalCode extends BaseDto +class PostalCode extends DataTransferObject { /** * Zipcode. diff --git a/src/BusinessLogic/Http/DTO/PostalZone.php b/src/BusinessLogic/Http/DTO/PostalZone.php index 7e14221c..6923ba9b 100644 --- a/src/BusinessLogic/Http/DTO/PostalZone.php +++ b/src/BusinessLogic/Http/DTO/PostalZone.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class PostalZone * * @package Packlink\BusinessLogic\Http\DTO */ -class PostalZone extends BaseDto +class PostalZone extends DataTransferObject { /** * ID of the postal zone. @@ -53,11 +53,11 @@ public static function fromArray(array $raw) { $instance = new static(); - $instance->id = static::getValue($raw, 'id'); - $instance->name = static::getValue($raw, 'name'); - $instance->hasPostalCodes = static::getValue($raw, 'hasPostalCodes', false); - $instance->isoCode = static::getValue($raw, 'isoCode'); - $instance->phonePrefix = static::getValue($raw, 'phonePrefix'); + $instance->id = static::getDataValue($raw, 'id'); + $instance->name = static::getDataValue($raw, 'name'); + $instance->hasPostalCodes = static::getDataValue($raw, 'hasPostalCodes', false); + $instance->isoCode = static::getDataValue($raw, 'isoCode'); + $instance->phonePrefix = static::getDataValue($raw, 'phonePrefix'); return $instance; } diff --git a/src/BusinessLogic/Http/DTO/Shipment.php b/src/BusinessLogic/Http/DTO/Shipment.php index 23a05cd4..ca7f5f69 100644 --- a/src/BusinessLogic/Http/DTO/Shipment.php +++ b/src/BusinessLogic/Http/DTO/Shipment.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class Shipment. * * @package Packlink\BusinessLogic\Http\DTO */ -class Shipment extends BaseDto +class Shipment extends DataTransferObject { /** * Shipment reference unique identifier. @@ -112,27 +112,27 @@ public function toArray() public static function fromArray(array $raw) { $shipment = new static(); - $date = static::getValue($raw, 'order_date'); + $date = static::getDataValue($raw, 'order_date'); if ($date) { $shipment->orderDate = \DateTime::createFromFormat('Y-m-d', $date); } - $shipment->reference = static::getValue($raw, 'packlink_reference'); - $shipment->shipmentCustomReference = static::getValue($raw, 'shipment_custom_reference'); - $shipment->service = static::getValue($raw, 'service'); - $shipment->serviceId = static::getValue($raw, 'service_id'); - $shipment->content = static::getValue($raw, 'content'); - $shipment->carrier = static::getValue($raw, 'carrier'); - $shipment->status = static::getValue($raw, 'state'); - $shipment->trackingCodes = static::getValue($raw, 'trackings', array()); - $shipment->price = static::getValue($raw, 'price', null); + $shipment->reference = static::getDataValue($raw, 'packlink_reference'); + $shipment->shipmentCustomReference = static::getDataValue($raw, 'shipment_custom_reference'); + $shipment->service = static::getDataValue($raw, 'service'); + $shipment->serviceId = static::getDataValue($raw, 'service_id'); + $shipment->content = static::getDataValue($raw, 'content'); + $shipment->carrier = static::getDataValue($raw, 'carrier'); + $shipment->status = static::getDataValue($raw, 'state'); + $shipment->trackingCodes = static::getDataValue($raw, 'trackings', array()); + $shipment->price = static::getDataValue($raw, 'price', null); if (is_array($shipment->price)) { - $shipment->price = static::getValue($shipment->price, 'base_price'); + $shipment->price = static::getDataValue($shipment->price, 'base_price'); } else { $shipment->price = 0.0; } - $shipment->carrierTrackingUrl = static::getValue($raw, 'tracking_url'); + $shipment->carrierTrackingUrl = static::getDataValue($raw, 'tracking_url'); return $shipment; } diff --git a/src/BusinessLogic/Http/DTO/ShipmentLabel.php b/src/BusinessLogic/Http/DTO/ShipmentLabel.php index ca07a104..28e28969 100644 --- a/src/BusinessLogic/Http/DTO/ShipmentLabel.php +++ b/src/BusinessLogic/Http/DTO/ShipmentLabel.php @@ -2,15 +2,15 @@ namespace Packlink\BusinessLogic\Http\DTO; +use Logeecom\Infrastructure\Data\DataTransferObject; use Logeecom\Infrastructure\ServiceRegister; use Logeecom\Infrastructure\Utility\TimeProvider; -use Packlink\BusinessLogic\DTO\BaseDto; /** * Class ShipmentLabel * @package Packlink\BusinessLogic\Http\DTO */ -class ShipmentLabel extends BaseDto +class ShipmentLabel extends DataTransferObject { /** * Link to PDF. @@ -59,9 +59,9 @@ public function __construct($link, $printed = false, $createTimestamp = 0) public static function fromArray(array $batchRaw) { return new static( - static::getValue($batchRaw, 'link'), - static::getValue($batchRaw, 'printed', false), - static::getValue($batchRaw, 'createTime', 0) + static::getDataValue($batchRaw, 'link'), + static::getDataValue($batchRaw, 'printed', false), + static::getDataValue($batchRaw, 'createTime', 0) ); } diff --git a/src/BusinessLogic/Http/DTO/ShippingService.php b/src/BusinessLogic/Http/DTO/ShippingService.php index b6408128..5a9bafd7 100644 --- a/src/BusinessLogic/Http/DTO/ShippingService.php +++ b/src/BusinessLogic/Http/DTO/ShippingService.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class ShippingService hold primary details about shipping service. * * @package Packlink\BusinessLogic\Http\DTO */ -class ShippingService extends BaseDto +class ShippingService extends DataTransferObject { /** * Service Id. @@ -91,15 +91,15 @@ public static function fromArray(array $raw) { $instance = new static(); - $instance->id = (int)self::getValue($raw, 'service_id'); - $instance->enabled = (bool)self::getValue($raw, 'enabled'); - $instance->carrierName = self::getValue($raw, 'carrier_name'); - $instance->serviceName = self::getValue($raw, 'service_name'); - $instance->logoUrl = self::getValue($raw, 'service_logo'); - $instance->departureDropOff = self::getValue($raw, 'departure_type') === 'drop-off'; - $instance->destinationDropOff = self::getValue($raw, 'destination_type') === 'drop-off'; - $instance->serviceDetails = self::getValue($raw, 'service_details', array()); - $instance->packlinkInfo = self::getValue($raw, 'packlink_info', array()); + $instance->id = (int)self::getDataValue($raw, 'service_id'); + $instance->enabled = (bool)self::getDataValue($raw, 'enabled'); + $instance->carrierName = self::getDataValue($raw, 'carrier_name'); + $instance->serviceName = self::getDataValue($raw, 'service_name'); + $instance->logoUrl = self::getDataValue($raw, 'service_logo'); + $instance->departureDropOff = self::getDataValue($raw, 'departure_type') === 'drop-off'; + $instance->destinationDropOff = self::getDataValue($raw, 'destination_type') === 'drop-off'; + $instance->serviceDetails = self::getDataValue($raw, 'service_details', array()); + $instance->packlinkInfo = self::getDataValue($raw, 'packlink_info', array()); return $instance; } diff --git a/src/BusinessLogic/Http/DTO/ShippingServiceDetails.php b/src/BusinessLogic/Http/DTO/ShippingServiceDetails.php index 0f9fb7d7..291d61c6 100644 --- a/src/BusinessLogic/Http/DTO/ShippingServiceDetails.php +++ b/src/BusinessLogic/Http/DTO/ShippingServiceDetails.php @@ -2,7 +2,7 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class ShippingServiceDetails holds information about delivery details for specific shipping service @@ -10,7 +10,7 @@ * * @package Packlink\BusinessLogic\Http\DTO */ -class ShippingServiceDetails extends BaseDto +class ShippingServiceDetails extends DataTransferObject { /** * Service Id. @@ -177,33 +177,33 @@ public static function fromArray(array $raw) { $instance = new static(); - $instance->id = self::getValue($raw, 'id'); - $instance->carrierName = self::getValue($raw, 'carrier_name'); - $instance->serviceName = self::getValue($raw, 'service_name'); - $instance->currency = self::getValue($raw, 'currency'); - $instance->country = self::getValue($raw, 'country'); - $instance->departureDropOff = self::getValue($raw, 'dropoff', false); - $instance->destinationDropOff = self::getValue($raw, 'delivery_to_parcelshop', false); - $instance->labelsRequired = self::getValue($raw, 'labels_required', false); - $instance->category = self::getValue($raw, 'category'); + $instance->id = self::getDataValue($raw, 'id'); + $instance->carrierName = self::getDataValue($raw, 'carrier_name'); + $instance->serviceName = self::getDataValue($raw, 'service_name'); + $instance->currency = self::getDataValue($raw, 'currency'); + $instance->country = self::getDataValue($raw, 'country'); + $instance->departureDropOff = self::getDataValue($raw, 'dropoff', false); + $instance->destinationDropOff = self::getDataValue($raw, 'delivery_to_parcelshop', false); + $instance->labelsRequired = self::getDataValue($raw, 'labels_required', false); + $instance->category = self::getDataValue($raw, 'category'); $instance->expressDelivery = $instance->category === 'express'; - $instance->transitTime = self::getValue($raw, 'transit_time'); - $instance->transitHours = (int)self::getValue($raw, 'transit_hours', 0); + $instance->transitTime = self::getDataValue($raw, 'transit_time'); + $instance->transitHours = (int)self::getDataValue($raw, 'transit_hours', 0); $instance->firstEstimatedDeliveryDate = \DateTime::createFromFormat( 'YYYY/MM/DD', - self::getValue($raw, 'first_estimated_delivery_date', '1970/01/01') + self::getDataValue($raw, 'first_estimated_delivery_date', '1970/01/01') ); /** @var array $prices */ - $prices = self::getValue($raw, 'price', array()); + $prices = self::getDataValue($raw, 'price', array()); if (!empty($prices)) { - $instance->totalPrice = self::getValue($prices, 'total_price', 0); - $instance->taxPrice = self::getValue($prices, 'tax_price', 0); - $instance->basePrice = self::getValue($prices, 'base_price', 0); + $instance->totalPrice = self::getDataValue($prices, 'total_price', 0); + $instance->taxPrice = self::getDataValue($prices, 'tax_price', 0); + $instance->basePrice = self::getDataValue($prices, 'base_price', 0); } - $instance->serviceInfo = self::getValue($raw, 'service_info', array()); - $instance->availableDates = self::getValue($raw, 'available_dates', array()); - $instance->national = self::getValue($raw, 'national', null); + $instance->serviceInfo = self::getDataValue($raw, 'service_info', array()); + $instance->availableDates = self::getDataValue($raw, 'available_dates', array()); + $instance->national = self::getDataValue($raw, 'national', null); return $instance; } diff --git a/src/BusinessLogic/Http/DTO/ShippingServiceSearch.php b/src/BusinessLogic/Http/DTO/ShippingServiceSearch.php index c0defa66..af9fc645 100644 --- a/src/BusinessLogic/Http/DTO/ShippingServiceSearch.php +++ b/src/BusinessLogic/Http/DTO/ShippingServiceSearch.php @@ -2,7 +2,7 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * This class holds search parameters that are used when searching for services @@ -10,7 +10,7 @@ * * @package Packlink\BusinessLogic\Http\DTO */ -class ShippingServiceSearch extends BaseDto +class ShippingServiceSearch extends DataTransferObject { /** * Service ID. Optional. @@ -115,21 +115,21 @@ public static function fromArray(array $raw) { $instance = new static(); - $instance->serviceId = self::getValue($raw, 'service_id', null); - $instance->fromCountry = self::getValue($raw, 'from[country]'); - $instance->fromZip = self::getValue($raw, 'from[zip]'); - $instance->toCountry = self::getValue($raw, 'to[country]'); - $instance->toZip = self::getValue($raw, 'to[zip]'); + $instance->serviceId = self::getDataValue($raw, 'service_id', null); + $instance->fromCountry = self::getDataValue($raw, 'from[country]'); + $instance->fromZip = self::getDataValue($raw, 'from[zip]'); + $instance->toCountry = self::getDataValue($raw, 'to[country]'); + $instance->toZip = self::getDataValue($raw, 'to[zip]'); $instance->packages = array(); $index = 0; while (array_key_exists("packages[$index][height]", $raw)) { $package = new Package(); - $package->height = self::getValue($raw, "packages[$index][height]"); - $package->width = self::getValue($raw, "packages[$index][width]"); - $package->length = self::getValue($raw, "packages[$index][length]"); - $package->weight = self::getValue($raw, "packages[$index][weight]"); + $package->height = self::getDataValue($raw, "packages[$index][height]"); + $package->width = self::getDataValue($raw, "packages[$index][width]"); + $package->length = self::getDataValue($raw, "packages[$index][length]"); + $package->weight = self::getDataValue($raw, "packages[$index][weight]"); $instance->packages[] = $package; $index++; diff --git a/src/BusinessLogic/Http/DTO/Tracking.php b/src/BusinessLogic/Http/DTO/Tracking.php index f1c6c989..10d7897d 100644 --- a/src/BusinessLogic/Http/DTO/Tracking.php +++ b/src/BusinessLogic/Http/DTO/Tracking.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class Tracking. * * @package Packlink\BusinessLogic\Http\DTO */ -class Tracking extends BaseDto +class Tracking extends DataTransferObject { /** * Timestamp of tracking entry. @@ -54,9 +54,9 @@ public function toArray() public static function fromArray(array $raw) { $tracking = new static(); - $tracking->timestamp = static::getValue($raw, 'timestamp'); - $tracking->description = static::getValue($raw, 'description'); - $tracking->city = static::getValue($raw, 'city'); + $tracking->timestamp = static::getDataValue($raw, 'timestamp'); + $tracking->description = static::getDataValue($raw, 'description'); + $tracking->city = static::getDataValue($raw, 'city'); return $tracking; } @@ -68,11 +68,10 @@ public static function fromArray(array $raw) * * @return static[] Array of transformed DTO objects. */ - public static function fromArrayBatch(array $batchRaw) + public static function fromBatch(array $batchRaw) { $batchRaw = (isset($batchRaw['history']) && is_array($batchRaw['history']) ? $batchRaw['history'] : array()); - /** @noinspection PhpIncompatibleReturnTypeInspection */ - return parent::fromArrayBatch($batchRaw); + return parent::fromBatch($batchRaw); } } diff --git a/src/BusinessLogic/Http/DTO/User.php b/src/BusinessLogic/Http/DTO/User.php index 91467ce4..0eb8e796 100644 --- a/src/BusinessLogic/Http/DTO/User.php +++ b/src/BusinessLogic/Http/DTO/User.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\Http\DTO; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class User. Represents Packlink User. * * @package Packlink\BusinessLogic\Http\DTO */ -class User extends BaseDto +class User extends DataTransferObject { /** * First name. @@ -62,10 +62,10 @@ public static function fromArray(array $raw) { $user = new static(); - $user->firstName = static::getValue($raw, 'name'); - $user->lastName = static::getValue($raw, 'surname'); - $user->email = static::getValue($raw, 'email'); - $user->country = static::getValue($raw, 'platform_country'); + $user->firstName = static::getDataValue($raw, 'name'); + $user->lastName = static::getDataValue($raw, 'surname'); + $user->email = static::getDataValue($raw, 'email'); + $user->country = static::getDataValue($raw, 'platform_country'); return $user; } diff --git a/src/BusinessLogic/Http/Proxy.php b/src/BusinessLogic/Http/Proxy.php index f089577c..c59904a5 100644 --- a/src/BusinessLogic/Http/Proxy.php +++ b/src/BusinessLogic/Http/Proxy.php @@ -82,7 +82,7 @@ public function __construct(Configuration $configService, HttpClient $client) public function getUsersParcelInfo() { $response = $this->call(HttpClient::HTTP_METHOD_GET, 'users/parcels'); - $data = $response->decodeBodyAsJson() ?: array(); + $data = $response->decodeBodyToArray() ?: array(); /** @noinspection PhpIncompatibleReturnTypeInspection */ /** @noinspection PhpUnhandledExceptionInspection */ @@ -102,7 +102,7 @@ public function getUsersParcelInfo() public function getUsersWarehouses() { $response = $this->call(HttpClient::HTTP_METHOD_GET, 'clients/warehouses'); - $data = $response->decodeBodyAsJson() ?: array(); + $data = $response->decodeBodyToArray() ?: array(); /** @noinspection PhpIncompatibleReturnTypeInspection */ /** @noinspection PhpUnhandledExceptionInspection */ @@ -129,7 +129,7 @@ public function register($data) $response = $this->call(HttpClient::HTTP_METHOD_POST, 'register', $data); - $data = $response->decodeBodyAsJson(); + $data = $response->decodeBodyToArray(); return !empty($data['token']) ? $data['token'] : ''; } @@ -147,7 +147,7 @@ public function getUserData() { $response = $this->call(HttpClient::HTTP_METHOD_GET, 'clients'); - return User::fromArray($response->decodeBodyAsJson()); + return User::fromArray($response->decodeBodyToArray()); } /** @@ -181,7 +181,7 @@ public function getLocations($serviceId, $countryCode, $postalCode) { $response = $this->call(HttpClient::HTTP_METHOD_GET, urlencode("dropoffs/$serviceId/$countryCode/$postalCode")); - return DropOff::fromArrayBatch($response->decodeBodyAsJson() ?: array()); + return DropOff::fromBatch($response->decodeBodyToArray() ?: array()); } /** @@ -210,7 +210,7 @@ public function searchLocations($platformCountry, $postalZone, $query) $response = $this->call(HttpClient::HTTP_METHOD_GET, $url); - return LocationInfo::fromArrayBatch($response->decodeBodyAsJson()); + return LocationInfo::fromBatch($response->decodeBodyToArray()); } /** @@ -228,7 +228,7 @@ public function getPostalCodes($countryCode, $zipCode) { $response = $this->call(HttpClient::HTTP_METHOD_GET, urlencode("locations/postalcodes/$countryCode/$zipCode")); - return PostalCode::fromArrayBatch($response->decodeBodyAsJson()); + return PostalCode::fromBatch($response->decodeBodyToArray()); } /** @@ -255,7 +255,7 @@ public function getPostalZones($countryCode, $lang = 'en') $response = $this->call(HttpClient::HTTP_METHOD_GET, $url); - $postalZones = PostalZone::fromArrayBatch($response->decodeBodyAsJson()); + $postalZones = PostalZone::fromBatch($response->decodeBodyToArray()); $postalZones = array_filter( $postalZones, @@ -288,12 +288,12 @@ public function getShippingServicesDeliveryDetails(ShippingServiceSearch $params $response = $this->call(HttpClient::HTTP_METHOD_GET, 'services?' . http_build_query($params->toArray())); - $body = $response->decodeBodyAsJson(); + $body = $response->decodeBodyToArray(); if (empty($body)) { return array(); } - $shippingDetails = ShippingServiceDetails::fromArrayBatch($body); + $shippingDetails = ShippingServiceDetails::fromBatch($body); foreach ($shippingDetails as $shippingDetail) { $shippingDetail->departureCountry = $params->fromCountry; @@ -319,7 +319,7 @@ public function getShippingServiceDetails($id) { $response = $this->call(HttpClient::HTTP_METHOD_GET, "services/available/$id/details"); - return ShippingService::fromArray($response->decodeBodyAsJson()); + return ShippingService::fromArray($response->decodeBodyToArray()); } /** @@ -338,7 +338,7 @@ public function sendDraft(Draft $draft) { $response = $this->call(HttpClient::HTTP_METHOD_POST, 'shipments', $draft->toArray()); - $result = $response->decodeBodyAsJson(); + $result = $response->decodeBodyToArray(); $reference = array_key_exists('reference', $result) ? $result['reference'] : ''; if (!$reference) { @@ -374,7 +374,7 @@ public function getShipment($referenceId) { $response = $this->getShipmentData($referenceId); - return $response !== null ? Shipment::fromArray($response->decodeBodyAsJson()) : null; + return $response !== null ? Shipment::fromArray($response->decodeBodyToArray()) : null; } /** @@ -392,7 +392,7 @@ public function getLabels($referenceId) { $response = $this->getShipmentData($referenceId, 'labels'); - return $response !== null ? $response->decodeBodyAsJson() : array(); + return $response !== null ? $response->decodeBodyToArray() : array(); } /** @@ -410,7 +410,7 @@ public function getTrackingInfo($referenceId) { $response = $this->getShipmentData($referenceId, 'track'); - return $response !== null ? Tracking::fromArrayBatch($response->decodeBodyAsJson()) : array(); + return $response !== null ? Tracking::fromBatch($response->decodeBodyToArray()) : array(); } /** @@ -509,7 +509,7 @@ protected function validateResponse(HttpResponse $response) { if (!$response->isSuccessful()) { $httpCode = $response->getStatus(); - $error = $message = $response->decodeBodyAsJson(); + $error = $message = $response->decodeBodyToArray(); if (is_array($error)) { $message = ''; if (isset($error['messages']) && is_array($error['messages'])) { diff --git a/src/BusinessLogic/Location/LocationService.php b/src/BusinessLogic/Location/LocationService.php index 8ead4473..b7909b57 100644 --- a/src/BusinessLogic/Location/LocationService.php +++ b/src/BusinessLogic/Location/LocationService.php @@ -6,7 +6,6 @@ use Logeecom\Infrastructure\ServiceRegister; use Packlink\BusinessLogic\BaseService; use Packlink\BusinessLogic\Configuration; -use Packlink\BusinessLogic\DTO\BaseDto; use Packlink\BusinessLogic\Http\DTO\Package; use Packlink\BusinessLogic\Http\DTO\ParcelInfo; use Packlink\BusinessLogic\Http\Proxy; @@ -144,7 +143,7 @@ public function searchLocations($country, $query) /** * Transforms collection of DTOs to an array response. * - * @param BaseDto[] $collection + * @param \Logeecom\Infrastructure\Data\DataTransferObject[] $collection * * @return array */ diff --git a/src/BusinessLogic/ORM/Contracts/ConditionallyDeletes.php b/src/BusinessLogic/ORM/Contracts/ConditionallyDeletes.php new file mode 100644 index 00000000..199b6b1d --- /dev/null +++ b/src/BusinessLogic/ORM/Contracts/ConditionallyDeletes.php @@ -0,0 +1,25 @@ +fields as $fieldName) { if ($fieldName === 'shipmentLabels' && !empty($data['shipmentLabels'])) { - $this->shipmentLabels = ShipmentLabel::fromArrayBatch($data['shipmentLabels']); + $this->shipmentLabels = ShipmentLabel::fromBatch($data['shipmentLabels']); } elseif ($fieldName === 'lastStatusUpdateTime' && !empty($data['lastStatusUpdateTime'])) { $this->lastStatusUpdateTime = $timeProvider->getDateTime($data['lastStatusUpdateTime']); } else { diff --git a/src/BusinessLogic/Registration/RegistrationLegalPolicy.php b/src/BusinessLogic/Registration/RegistrationLegalPolicy.php index 6ad640af..0cb8de16 100644 --- a/src/BusinessLogic/Registration/RegistrationLegalPolicy.php +++ b/src/BusinessLogic/Registration/RegistrationLegalPolicy.php @@ -80,10 +80,10 @@ public static function fromArray(array $raw) /** @var static $instance */ $instance = parent::fromArray($raw); - $instance->isDataProcessingAccepted = static::getValue($raw, 'data_processing'); - $instance->isTermsAccepted = static::getValue($raw, 'terms_and_conditions'); - $instance->isMarketingEmailsAccepted = static::getValue($raw, 'marketing_emails'); - $instance->isMarketingCallsAccepted = static::getValue($raw, 'marketing_calls'); + $instance->isDataProcessingAccepted = static::getDataValue($raw, 'data_processing'); + $instance->isTermsAccepted = static::getDataValue($raw, 'terms_and_conditions'); + $instance->isMarketingEmailsAccepted = static::getDataValue($raw, 'marketing_emails'); + $instance->isMarketingCallsAccepted = static::getDataValue($raw, 'marketing_calls'); return $instance; } diff --git a/src/BusinessLogic/Registration/RegistrationRequest.php b/src/BusinessLogic/Registration/RegistrationRequest.php index 96f909d7..4aabad68 100644 --- a/src/BusinessLogic/Registration/RegistrationRequest.php +++ b/src/BusinessLogic/Registration/RegistrationRequest.php @@ -175,8 +175,8 @@ public static function fromArray(array $raw) /** @var static $instance */ $instance = parent::fromArray($raw); - $instance->estimatedDeliveryVolume = static::getValue($raw, 'estimated_delivery_volume'); - $instance->platformCountry = static::getValue($raw, 'platform_country'); + $instance->estimatedDeliveryVolume = static::getDataValue($raw, 'estimated_delivery_volume'); + $instance->platformCountry = static::getDataValue($raw, 'platform_country'); $instance->policies = RegistrationLegalPolicy::fromArray($raw['policies']); return $instance; diff --git a/src/BusinessLogic/Scheduler/ScheduleCheckTask.php b/src/BusinessLogic/Scheduler/ScheduleCheckTask.php index fbaf1f2a..164ff1fd 100644 --- a/src/BusinessLogic/Scheduler/ScheduleCheckTask.php +++ b/src/BusinessLogic/Scheduler/ScheduleCheckTask.php @@ -75,7 +75,7 @@ public function execute() continue; } - $queueService->enqueue($schedule->getQueueName(), $task, $schedule->getContext()); + $queueService->enqueue($schedule->getQueueName(), $task, $schedule->getContext(), $task->getPriority()); if ($schedule->isRecurring()) { $schedule->setNextSchedule(); diff --git a/src/BusinessLogic/Scheduler/ScheduleTickHandler.php b/src/BusinessLogic/Scheduler/ScheduleTickHandler.php index 4ec6fad8..5b6b261e 100644 --- a/src/BusinessLogic/Scheduler/ScheduleTickHandler.php +++ b/src/BusinessLogic/Scheduler/ScheduleTickHandler.php @@ -31,7 +31,12 @@ public function handle() if ($task === null || $task->getQueueTimestamp() + $threshold < time()) { $task = new ScheduleCheckTask(); try { - $queueService->enqueue($configService->getSchedulerQueueName(), $task); + $queueService->enqueue( + $configService->getSchedulerQueueName(), + $task, + $configService->getContext(), + $task->getPriority() + ); } catch (QueueStorageUnavailableException $ex) { Logger::logDebug( 'Failed to enqueue task ' . $task->getType(), diff --git a/src/BusinessLogic/ShipmentDraft/Objects/ShipmentDraftStatus.php b/src/BusinessLogic/ShipmentDraft/Objects/ShipmentDraftStatus.php index 699bd133..6fd5acef 100644 --- a/src/BusinessLogic/ShipmentDraft/Objects/ShipmentDraftStatus.php +++ b/src/BusinessLogic/ShipmentDraft/Objects/ShipmentDraftStatus.php @@ -2,14 +2,14 @@ namespace Packlink\BusinessLogic\ShipmentDraft\Objects; -use Packlink\BusinessLogic\DTO\BaseDto; +use Logeecom\Infrastructure\Data\DataTransferObject; /** * Class ShipmentDraftStatus. * * @package Packlink\BusinessLogic\ShipmentDraft\Objects */ -class ShipmentDraftStatus extends BaseDto +class ShipmentDraftStatus extends DataTransferObject { /** * Represents the status where create shipment draft task is not created. @@ -42,8 +42,8 @@ class ShipmentDraftStatus extends BaseDto public static function fromArray(array $raw) { $me = new static(); - $me->status = static::getValue($raw, 'status'); - $me->message = static::getValue($raw, 'message'); + $me->status = static::getDataValue($raw, 'status'); + $me->message = static::getDataValue($raw, 'message'); return $me; } diff --git a/src/BusinessLogic/ShipmentDraft/ShipmentDraftService.php b/src/BusinessLogic/ShipmentDraft/ShipmentDraftService.php index f02f09e8..5b6f89c5 100644 --- a/src/BusinessLogic/ShipmentDraft/ShipmentDraftService.php +++ b/src/BusinessLogic/ShipmentDraft/ShipmentDraftService.php @@ -3,16 +3,20 @@ namespace Packlink\BusinessLogic\ShipmentDraft; use Logeecom\Infrastructure\Configuration\Configuration; +use Logeecom\Infrastructure\ORM\Interfaces\RepositoryInterface; use Logeecom\Infrastructure\ORM\RepositoryRegistry; use Logeecom\Infrastructure\ServiceRegister; use Logeecom\Infrastructure\TaskExecution\QueueItem; use Logeecom\Infrastructure\TaskExecution\QueueService; use Logeecom\Infrastructure\Utility\TimeProvider; use Packlink\BusinessLogic\BaseService; +use Packlink\BusinessLogic\Scheduler\Models\DailySchedule; use Packlink\BusinessLogic\Scheduler\Models\HourlySchedule; use Packlink\BusinessLogic\Scheduler\Models\Schedule; use Packlink\BusinessLogic\ShipmentDraft\Objects\ShipmentDraftStatus; +use Packlink\BusinessLogic\ShippingMethod\Utility\ShipmentStatus; use Packlink\BusinessLogic\Tasks\SendDraftTask; +use Packlink\BusinessLogic\Tasks\UpdateShipmentDataTask; /** * Class ShipmentDraftService. @@ -59,14 +63,19 @@ public function enqueueCreateShipmentDraftTask($orderId, $isDelayed = false, $de $draftTaskMapService->createOrderTaskMap($orderId); } - /** @var Configuration $configService */ - $configService = ServiceRegister::getService(Configuration::CLASS_NAME); + /** @var \Packlink\BusinessLogic\Configuration $configService */ + $configService = $this->getConfigService(); $sendDraftTask = new SendDraftTask($orderId); if (!$isDelayed) { /** @var QueueService $queue */ $queue = ServiceRegister::getService(QueueService::CLASS_NAME); - $queue->enqueue($configService->getDefaultQueueName(), $sendDraftTask, $configService->getContext()); + $queue->enqueue( + $configService->getDefaultQueueName(), + $sendDraftTask, + $configService->getContext(), + $sendDraftTask->getPriority() + ); if ($sendDraftTask->getExecutionId() !== null) { $draftTaskMapService->setExecutionId($orderId, $sendDraftTask->getExecutionId()); @@ -74,6 +83,11 @@ public function enqueueCreateShipmentDraftTask($orderId, $isDelayed = false, $de } else { $this->enqueueDelayedTask($sendDraftTask, $delayInterval); } + + if (!$configService->isFirstShipmentDraftCreated()) { + $this->enqueueShipmentSchedules(); + $configService->setFirstShipmentDraftCreated(); + } } /** @@ -139,4 +153,86 @@ protected function enqueueDelayedTask(SendDraftTask $task, $delayInterval) RepositoryRegistry::getRepository(Schedule::CLASS_NAME)->save($schedule); } + + /** + * Enqueues shipment related schedules. + * + * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException + */ + private function enqueueShipmentSchedules() + { + $repository = RepositoryRegistry::getRepository(Schedule::getClassName()); + $firstStart = rand(0, 59); + $secondStart = ($firstStart + 30) % 60; + + // Schedule hourly task for updating shipment info - start at full hour + $this->scheduleUpdatePendingShipmentsData($repository, $firstStart); + + // Schedule hourly task for updating shipment info - start at half hour + $this->scheduleUpdatePendingShipmentsData($repository, $secondStart); + + // Schedule daily task for updating shipment info - start at 11:00 UTC hour + $this->scheduleUpdateInProgressShipments($repository, 11, rand(0, 59)); + } + + /** + * Creates hourly task for updating shipment data for pending shipments. + * + * @param RepositoryInterface $repository Scheduler repository. + * @param int $minute Starting minute for the task. + */ + protected function scheduleUpdatePendingShipmentsData(RepositoryInterface $repository, $minute) + { + $hourlyStatuses = array( + ShipmentStatus::STATUS_PENDING, + ); + + $schedule = new HourlySchedule( + new UpdateShipmentDataTask($hourlyStatuses), + $this->getConfigService()->getDefaultQueueName(), + $this->getConfigService()->getContext() + ); + + $schedule->setMinute($minute); + $schedule->setNextSchedule(); + $repository->save($schedule); + } + + /** + * Creates daily task for updating shipment data for shipments in progress. + * + * @param RepositoryInterface $repository Schedule repository. + * @param int $hour Hour of the day when schedule should be executed. + * @param int $minute Minute of the schedule. + */ + protected function scheduleUpdateInProgressShipments(RepositoryInterface $repository, $hour, $minute) + { + $dailyStatuses = array( + ShipmentStatus::STATUS_IN_TRANSIT, + ShipmentStatus::STATUS_READY, + ShipmentStatus::STATUS_ACCEPTED, + ); + + $schedule = new DailySchedule( + new UpdateShipmentDataTask($dailyStatuses), + $this->getConfigService()->getDefaultQueueName(), + $this->getConfigService()->getContext() + ); + + $schedule->setHour($hour); + $schedule->setMinute($minute); + $schedule->setNextSchedule(); + + $repository->save($schedule); + } + + /** + * Retrieves config service. + * + * @return Configuration | object + */ + private function getConfigService() + { + return ServiceRegister::getService(Configuration::CLASS_NAME); + } } diff --git a/src/BusinessLogic/Tasks/BatchTaskCleanupTask.php b/src/BusinessLogic/Tasks/BatchTaskCleanupTask.php new file mode 100644 index 00000000..978ad6d3 --- /dev/null +++ b/src/BusinessLogic/Tasks/BatchTaskCleanupTask.php @@ -0,0 +1,171 @@ +taskStatuses = $taskStatuses; + $this->taskTypes = $taskTypes; + } + + /** + * @inheritdoc + */ + public function serialize() + { + return Serializer::serialize(array($this->taskStatuses, $this->taskTypes)); + } + + /** + * @inheritdoc + */ + public function unserialize($serialized) + { + list($this->taskStatuses, $this->taskTypes) = Serializer::unserialize($serialized); + } + + /** + * Transforms serializable object into an array. + * + * @return array Array representation of a serializable object. + */ + public function toArray() + { + return array( + 'taskStatuses' => $this->taskStatuses, + 'taskTypes' => $this->taskTypes, + ); + } + + /** + * Transforms array into an serializable object, + * + * @param array $array Data that is used to instantiate serializable object. + * + * @return \Logeecom\Infrastructure\Serializer\Interfaces\Serializable + * Instance of serialized object. + */ + public static function fromArray(array $array) + { + return new static($array['taskStatuses'], $array['taskTypes']); + } + + /** + * Executes the task. + * + * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException + * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException + * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException + */ + public function execute() + { + Logger::logDebug('Task types to be deleted:', 'Core', $this->taskTypes); + + $repository = RepositoryRegistry::getQueueItemRepository(); + if (!$repository instanceof ConditionallyDeletes) { + throw new AbortTaskExecutionException( + 'QueueItemRepository must implement ConditionallyDeletes ' + . 'interface before it can utilize BatchTaskCleanupTask.' + ); + } + + $query = $this->getDeleteQuery(); + + $this->reportProgress(10); + + $repository->deleteWhere($query); + + $this->reportProgress(100); + } + + /** + * Retrieves query that will be used identify tasks that must be deleted. + * + * @return \Logeecom\Infrastructure\ORM\QueryFilter\QueryFilter + * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException + */ + private function getDeleteQuery() + { + $query = new QueryFilter(); + $query->where('queueTime', Operators::LESS_THAN, $this->getAgeCutOff()); + $query->where('status', Operators::IN, $this->taskStatuses); + + if (!empty($this->taskTypes)) { + $query->where('taskType', Operators::IN, $this->getTaskTypes()); + } + + return $query; + } + + /** + * Retrieves date time before which tasks will be deleted. + * + * @return \DateTime + */ + private function getAgeCutOff() + { + /** @noinspection PhpPossiblePolymorphicInvocationInspection */ + $maxAge = $this->getConfigService()->getMaxTaskAge(); + $currentDateTime = $this->getTimeProvider()->getDateTime(time()); + + return $currentDateTime->modify("-$maxAge day"); + } + + /** + * Retrieves list of task types that must be deleted. + * + * @return array + */ + private function getTaskTypes() + { + return array_unique(array_merge($this->taskTypes, array($this->getType()))); + } + + /** + * Retrieves time provider. + * + * @return TimeProvider | object + */ + private function getTimeProvider() + { + return ServiceRegister::getService(TimeProvider::CLASS_NAME); + } +} \ No newline at end of file diff --git a/src/BusinessLogic/Tasks/SendDraftTask.php b/src/BusinessLogic/Tasks/SendDraftTask.php index bfd95353..89de2557 100644 --- a/src/BusinessLogic/Tasks/SendDraftTask.php +++ b/src/BusinessLogic/Tasks/SendDraftTask.php @@ -7,6 +7,7 @@ use Logeecom\Infrastructure\Serializer\Serializer; use Logeecom\Infrastructure\ServiceRegister; use Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException; +use Logeecom\Infrastructure\TaskExecution\Interfaces\Priority; use Logeecom\Infrastructure\TaskExecution\Task; use Packlink\BusinessLogic\Http\Proxy; use Packlink\BusinessLogic\Order\Exceptions\EmptyOrderException; @@ -103,6 +104,16 @@ public function unserialize($serialized) list($this->orderId) = Serializer::unserialize($serialized); } + /** + * Retrieves task priority. + * + * @return int Task priority. + */ + public function getPriority() + { + return Priority::HIGH; + } + /** * Runs task logic. * diff --git a/src/BusinessLogic/Tasks/TaskCleanupTask.php b/src/BusinessLogic/Tasks/TaskCleanupTask.php index 96de5bfb..b472d386 100644 --- a/src/BusinessLogic/Tasks/TaskCleanupTask.php +++ b/src/BusinessLogic/Tasks/TaskCleanupTask.php @@ -7,6 +7,7 @@ use Logeecom\Infrastructure\ORM\RepositoryRegistry; use Logeecom\Infrastructure\Serializer\Serializer; use Logeecom\Infrastructure\ServiceRegister; +use Logeecom\Infrastructure\TaskExecution\Interfaces\Priority; use Logeecom\Infrastructure\TaskExecution\QueueItem; use Logeecom\Infrastructure\TaskExecution\Task; use Logeecom\Infrastructure\Utility\TimeProvider; @@ -103,6 +104,16 @@ public function unserialize($serialized) list($this->taskType, $this->taskStatuses, $this->taskAge) = array_values(Serializer::unserialize($serialized)); } + /** + * Retrieves task priority. + * + * @return int Task priority. + */ + public function getPriority() + { + return Priority::LOW; + } + /** * Runs task logic. * diff --git a/src/BusinessLogic/User/UserAccountService.php b/src/BusinessLogic/User/UserAccountService.php index 977f7509..1b584e52 100644 --- a/src/BusinessLogic/User/UserAccountService.php +++ b/src/BusinessLogic/User/UserAccountService.php @@ -7,7 +7,6 @@ use Logeecom\Infrastructure\ORM\Interfaces\RepositoryInterface; use Logeecom\Infrastructure\ORM\RepositoryRegistry; use Logeecom\Infrastructure\ServiceRegister; -use Logeecom\Infrastructure\TaskExecution\QueueItem; use Logeecom\Infrastructure\TaskExecution\QueueService; use Packlink\BusinessLogic\BaseService; use Packlink\BusinessLogic\Configuration; @@ -15,14 +14,8 @@ use Packlink\BusinessLogic\Http\DTO\Analytics; use Packlink\BusinessLogic\Http\DTO\User; use Packlink\BusinessLogic\Http\Proxy; -use Packlink\BusinessLogic\Scheduler\Models\DailySchedule; -use Packlink\BusinessLogic\Scheduler\Models\HourlySchedule; use Packlink\BusinessLogic\Scheduler\Models\Schedule; use Packlink\BusinessLogic\Scheduler\Models\WeeklySchedule; -use Packlink\BusinessLogic\Scheduler\ScheduleCheckTask; -use Packlink\BusinessLogic\ShippingMethod\Utility\ShipmentStatus; -use Packlink\BusinessLogic\Tasks\TaskCleanupTask; -use Packlink\BusinessLogic\Tasks\UpdateShipmentDataTask; use Packlink\BusinessLogic\Tasks\UpdateShippingServicesTask; /** @@ -85,6 +78,7 @@ public function login($apiKey) } $this->createSchedules(); + $this->getConfigService()->setFirstShipmentDraftCreated(false); return true; } @@ -226,18 +220,6 @@ protected function createSchedules() $repository = RepositoryRegistry::getRepository(Schedule::CLASS_NAME); $this->scheduleUpdateShipmentServicesTask($repository); - - // Schedule hourly task for updating shipment info - start at full hour - $this->scheduleUpdatePendingShipmentsData($repository, 0); - - // Schedule hourly task for updating shipment info - start at half hour - $this->scheduleUpdatePendingShipmentsData($repository, 30); - - // Schedule daily task for updating shipment info - start at 11:00 UTC hour - $this->scheduleUpdateInProgressShipments($repository, 11); - - // schedule hourly queue cleanup - $this->scheduleTaskCleanup($repository); } /** @@ -253,75 +235,9 @@ protected function scheduleUpdateShipmentServicesTask(RepositoryInterface $repos $this->getConfigService()->getContext() ); - $schedule->setDay(1); - $schedule->setHour(2); - $schedule->setNextSchedule(); - $repository->save($schedule); - } - - /** - * Creates hourly task for updating shipment data for pending shipments. - * - * @param RepositoryInterface $repository Scheduler repository. - * @param int $minute Starting minute for the task. - */ - protected function scheduleUpdatePendingShipmentsData(RepositoryInterface $repository, $minute) - { - $hourlyStatuses = array( - ShipmentStatus::STATUS_PENDING, - ); - - $schedule = new HourlySchedule( - new UpdateShipmentDataTask($hourlyStatuses), - $this->getConfigService()->getDefaultQueueName(), - $this->getConfigService()->getContext() - ); - - $schedule->setMinute($minute); - $schedule->setNextSchedule(); - $repository->save($schedule); - } - - /** - * Creates daily task for updating shipment data for shipments in progress. - * - * @param RepositoryInterface $repository Schedule repository. - * @param int $hour Hour of the day when schedule should be executed. - */ - protected function scheduleUpdateInProgressShipments(RepositoryInterface $repository, $hour) - { - $dailyStatuses = array( - ShipmentStatus::STATUS_IN_TRANSIT, - ShipmentStatus::STATUS_READY, - ShipmentStatus::STATUS_ACCEPTED, - ); - - $schedule = new DailySchedule( - new UpdateShipmentDataTask($dailyStatuses), - $this->getConfigService()->getDefaultQueueName(), - $this->getConfigService()->getContext() - ); - - $schedule->setHour($hour); - $schedule->setNextSchedule(); - - $repository->save($schedule); - } - - /** - * Creates hourly task for cleaning up the database queue for completed items. - * - * @param RepositoryInterface $repository Scheduler repository. - */ - protected function scheduleTaskCleanup(RepositoryInterface $repository) - { - $schedule = new HourlySchedule( - new TaskCleanupTask(ScheduleCheckTask::getClassName(), array(QueueItem::COMPLETED), 3600), - $this->getConfigService()->getDefaultQueueName(), - $this->getConfigService()->getContext() - ); - - $schedule->setMinute(10); + $schedule->setDay(rand(1, 7)); + $schedule->setHour(rand(0, 5)); + $schedule->setMinute(rand(0, 59)); $schedule->setNextSchedule(); $repository->save($schedule); } diff --git a/src/BusinessLogic/Warehouse/Warehouse.php b/src/BusinessLogic/Warehouse/Warehouse.php index 79bd314c..06b6cc93 100644 --- a/src/BusinessLogic/Warehouse/Warehouse.php +++ b/src/BusinessLogic/Warehouse/Warehouse.php @@ -138,8 +138,8 @@ public static function fromArray(array $raw) { /** @var static $instance */ $instance = parent::fromArray($raw); - $instance->default = static::getValue($raw, 'default_selection', false); - $instance->postalCode = static::getValue($raw, 'postal_code'); + $instance->default = static::getDataValue($raw, 'default_selection', false); + $instance->postalCode = static::getDataValue($raw, 'postal_code'); return $instance; } diff --git a/src/Infrastructure/Configuration/Configuration.php b/src/Infrastructure/Configuration/Configuration.php index 1a836a88..feff2a8b 100644 --- a/src/Infrastructure/Configuration/Configuration.php +++ b/src/Infrastructure/Configuration/Configuration.php @@ -5,7 +5,7 @@ namespace Logeecom\Infrastructure\Configuration; -use Logeecom\Infrastructure\Http\DTO\OptionsDTO; +use Logeecom\Infrastructure\Http\DTO\Options; use Logeecom\Infrastructure\ORM\QueryFilter\QueryFilter; use Logeecom\Infrastructure\ORM\RepositoryRegistry; use Logeecom\Infrastructure\Singleton; @@ -38,6 +38,10 @@ abstract class Configuration extends Singleton * Default HTTP method to use for async call. */ const ASYNC_CALL_METHOD = 'POST'; + /** + * Default batch size for the asynchronous execution. + */ + const DEFAULT_ASYNC_STARTER_BATCH_SIZE = 8; /** * System user context. * @@ -150,6 +154,26 @@ public function setDebugModeEnabled($status) $this->saveConfigValue('debugModeEnabled', (bool)$status); } + /** + * Retrieves async starter batch size. + * + * @return int Async starter batch size. + */ + public function getAsyncStarterBatchSize() + { + return $this->getConfigValue('asyncStarterBatchSize', static::DEFAULT_ASYNC_STARTER_BATCH_SIZE); + } + + /** + * Sets async process batch size. + * + * @param int $size + */ + public function setAsyncStarterBatchSize($size) + { + $this->saveConfigValue('asyncStarterBatchSize', $size); + } + /** * Returns debug mode status. * @@ -340,13 +364,13 @@ public function setAutoConfigurationState($state) * * @param string $domain A domain for which to return configuration options. * - * @return \Logeecom\Infrastructure\Http\DTO\OptionsDTO[] + * @return \Logeecom\Infrastructure\Http\DTO\Options[] */ public function getHttpConfigurationOptions($domain) { $data = json_decode($this->getConfigValue('httpConfigurationOptions', '[]'), true); if (isset($data[$domain])) { - return OptionsDTO::fromArrayBatch($data[$domain]); + return Options::fromBatch($data[$domain]); } return array(); @@ -357,7 +381,7 @@ public function getHttpConfigurationOptions($domain) * * @param string $domain A domain for which to save configuration options. * - * @param OptionsDTO[] $options HTTP configuration options + * @param Options[] $options HTTP configuration options */ public function setHttpConfigurationOptions($domain, array $options) { diff --git a/src/Infrastructure/Data/DataTransferObject.php b/src/Infrastructure/Data/DataTransferObject.php new file mode 100644 index 00000000..44410ebd --- /dev/null +++ b/src/Infrastructure/Data/DataTransferObject.php @@ -0,0 +1,66 @@ + $item) { + $result[$index] = static::fromArray($item); + } + + return $result; + } + + /** + * Transforms data transfer object to array. + * + * @return array Array representation of data transfer object. + */ + abstract public function toArray(); + + /** + * Retrieves value from raw data. + * + * @param array $rawData Raw DTO data. + * @param string $key Data key. + * @param string $default Default value. + * + * @return mixed + */ + protected static function getDataValue(array $rawData, $key, $default = '') + { + return isset($rawData[$key]) ? $rawData[$key]: $default; + } +} \ No newline at end of file diff --git a/src/Infrastructure/Data/Transformer.php b/src/Infrastructure/Data/Transformer.php new file mode 100644 index 00000000..9dad6e30 --- /dev/null +++ b/src/Infrastructure/Data/Transformer.php @@ -0,0 +1,64 @@ +toArray(); + } + + /** + * Transforms a batch of transformable object. + * + * @param \Logeecom\Infrastructure\Data\DataTransferObject[] $batch Batch of transformable objects. + * + * @return array Batch of transformed objects. + */ + public static function batchTransform($batch) + { + $result = array(); + + if (!is_array($batch)) { + return $result; + } + + foreach ($batch as $index => $transformable) { + $result[$index] = static::transform($transformable); + } + + return $result; + } + + /** + * Trims empty arrays or null values. + * + * @param array $data + */ + protected static function trim(array &$data) + { + foreach ($data as $key => $value) { + if (is_array($value)) { + static::trim($data[$key]); + } + + if ($value === null || (is_array($value) && empty($value))) { + unset($data[$key]); + } + } + } +} \ No newline at end of file diff --git a/src/Infrastructure/Http/AsyncSocketHttpClient.php b/src/Infrastructure/Http/AsyncSocketHttpClient.php new file mode 100644 index 00000000..ad7ef329 --- /dev/null +++ b/src/Infrastructure/Http/AsyncSocketHttpClient.php @@ -0,0 +1,189 @@ +adjustUrlIfNeeded($url); + $urlDetails = parse_url($url); + + if ($urlDetails === false) { + throw new HttpRequestException('Unable to parse request url.'); + } + + $transferProtocol = $this->getTransferProtocol($urlDetails); + $port = $this->getTargetPort($urlDetails); + $path = $this->getPath($urlDetails); + $payload = $this->getRequestPayload(strtoupper($method), $urlDetails['host'], $path, $headers, $body); + $timeOut = $this->getRequestTimeOut(); + + $this->executeRequest($transferProtocol, $urlDetails['host'], $port, $timeOut, $payload); + } + + /** + * Deduces transfer protocol based ont the url scheme. + * + * @param array $urlDetails URL details formatted as the output of the parse_url method. + * + * @return string Returns ssl:// if scheme is HTTPS, tcp:// otherwise. + */ + protected function getTransferProtocol(array $urlDetails) + { + if ($urlDetails['scheme'] === 'https') { + return 'tls://'; + } + + return 'tcp://'; + } + + /** + * Provides request port based on the url details. + * + * If the port is defined in the URL returns defined port; + * Otherwise, if the scheme is HTTPS returns 443; + * Otherwise, returns 80. + * + * @param array $urlDetails URL details formatted as the output of the parse_url method. + * + * @return int Request port. + */ + protected function getTargetPort(array $urlDetails) + { + if (!empty($urlDetails['port'])) { + return $urlDetails['port']; + } + + if ($urlDetails['scheme'] === 'https') { + return 443; + } + + return 80; + } + + /** + * Retrieves request path based on url details. + * + * @param array $urlDetails URL details formatted as the output of the parse_url method. + * + * @return string Request path. + */ + protected function getPath(array $urlDetails) + { + return !empty($urlDetails['path']) ? $urlDetails['path'] : '/'; + } + + /** + * Retrieves request time out in seconds. + * + * @return int Request timeout in seconds. + */ + protected function getRequestTimeOut() + { + $timeout = $this->getConfigService()->getAsyncRequestTimeout(); + + return !empty($timeout) ? $timeout : static::DEFAULT_ASYNC_REQUEST_TIMEOUT; + } + + /** + * Generates request payload in accordance with the HTTP 1.1. + * + * @param string $method Request HTTP method. + * @param string $host Request host. + * @param string $path Request path. + * @param array $headers List of request headers. + * @param string $body Request body. + * + * @return string + */ + protected function getRequestPayload($method, $host, $path, array $headers, $body) + { + $payload = "$method $path HTTP/1.1\r\n"; + $payload .= "Host: $host\r\n"; + + foreach ($headers as $header => $value) { + $payload .= $header . (!empty($value) ? ": $value" : '') . "\r\n"; + } + + $payload .= "Content-Length: " . strlen($body) . "\r\n"; + $payload .= "Connection: close\r\n\r\n"; + + $payload .= $body . "\r\n\r\n"; + + return $payload; + } + + /** + * Executes request by writing to the php web socket. + * + * @param string $transferProtocol One of 'ssl://' or 'tcp://'. + * @param string $host Request host. + * @param int $port Destination port. + * @param int $timeOut Request timeout in seconds. + * @param string $payload Payload to be written to the socket. + * + * @throws \Logeecom\Infrastructure\Http\Exceptions\HttpRequestException Thrown when the request + * is not completed successfully. + */ + protected function executeRequest($transferProtocol, $host, $port, $timeOut, $payload) + { + $socket = pfsockopen($transferProtocol . $host, $port, $errorCode, $errorMsg, $timeOut); + if ($socket === false) { + throw new HttpRequestException($errorMsg, $errorCode); + } + + $writeResult = fwrite($socket, $payload); + if ($writeResult === false) { + throw new HttpRequestException('Unable to write to php socket.'); + } + + // Even though php reports that the write has been completed + // The data is not necessarily been written + // We must wait to make sure that the write is actually complete + usleep(self::FWRITE_SLEEP_USECONDS); + + fclose($socket); + } +} diff --git a/src/Infrastructure/Http/CurlHttpClient.php b/src/Infrastructure/Http/CurlHttpClient.php index 9f428332..6b2ff3c8 100644 --- a/src/Infrastructure/Http/CurlHttpClient.php +++ b/src/Infrastructure/Http/CurlHttpClient.php @@ -2,7 +2,7 @@ namespace Logeecom\Infrastructure\Http; -use Logeecom\Infrastructure\Http\DTO\OptionsDTO; +use Logeecom\Infrastructure\Http\DTO\Options; use Logeecom\Infrastructure\Http\Exceptions\HttpCommunicationException; use Logeecom\Infrastructure\Logger\Logger; @@ -56,35 +56,6 @@ class CurlHttpClient extends HttpClient */ private $curlSession; - /** - * Aborts async process request after first byte of the request is uploaded. - * - * @param resource $curlResource cURL resource. - * @param int $downloadTotal Total number of bytes expected to be downloaded in transfer. - * @param int $downloadSoFar Number of bytes downloaded so far. - * @param int $uploadTotal Total number of bytes expected to be uploaded in this transfer. - * @param int $uploadedSoFar Number of bytes uploaded so far. - * - * @return int If non-zero value is returned, underlying curl transfer will be aborted. - * @see https://www.php.net/manual/en/function.curl-setopt.php CURLOPT_PROGRESSFUNCTION config option - * @noinspection PhpUnusedParameterInspection - */ - public function abortAfterAsyncRequestCallback( - $curlResource, - $downloadTotal, - $downloadSoFar, - $uploadTotal, - $uploadedSoFar - ) { - if ($uploadTotal === 0 || ($uploadTotal !== $uploadedSoFar)) { - // Signal curl library to continue until upload is still in progress. - return 0; - } - - // Abort as soon as the upload is done. For an async request, we do not need to wait for a response. - return 1; - } - /** * Create and send request. * @@ -285,18 +256,6 @@ protected function setCurlSessionOptionsForAsynchronousRequest() // Timeout super fast once connected, so it goes into async. $asyncRequestTimeout = $this->getConfigService()->getAsyncRequestTimeout(); $this->curlOptions[CURLOPT_TIMEOUT_MS] = $asyncRequestTimeout ?: static::DEFAULT_ASYNC_REQUEST_TIMEOUT; - - if ($this->getConfigService()->isAsyncRequestWithProgress()) { - // Use higher request timeout value by default if progress callback mechanism is used for async request - // aborting. Pay attention that fast timout is not desired in this case because request can go in timout - // before request upload finishes, therefore, making async requests less stable that it needs to be. - if (!$asyncRequestTimeout) { - $this->curlOptions[CURLOPT_TIMEOUT_MS] = static::DEFAULT_ASYNC_REQUEST_WITH_PROGRESS_TIMEOUT; - } - - $this->curlOptions[CURLOPT_NOPROGRESS] = false; - $this->curlOptions[CURLOPT_PROGRESSFUNCTION] = array($this, 'abortAfterAsyncRequestCallback'); - } } /** @@ -358,7 +317,7 @@ function ($curl, $header) use (&$headers) { * @param string $url Request URL. Full URL where request should be sent. * * @return array - * Array of additional options combinations. Each array item should be an array of OptionsDTO instances. + * Array of additional options combinations. Each array item should be an array of Options instances. */ protected function getAutoConfigurationOptionsCombinations($method, $url) { @@ -368,10 +327,10 @@ protected function getAutoConfigurationOptionsCombinations($method, $url) * CURLOPT_FOLLOWLOCATION => false (default is true) * SWITCH_PROTOCOL => This is not a cURL option and is treated differently. Default is false. */ - $switchProtocol = new OptionsDTO(static::SWITCH_PROTOCOL, true); - $ipVersion = new OptionsDTO(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + $switchProtocol = new Options(static::SWITCH_PROTOCOL, true); + $ipVersion = new Options(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); if ($this->followLocation) { - $followLocation = new OptionsDTO(CURLOPT_FOLLOWLOCATION, false); + $followLocation = new Options(CURLOPT_FOLLOWLOCATION, false); return array( array($switchProtocol), diff --git a/src/Infrastructure/Http/DTO/OptionsDTO.php b/src/Infrastructure/Http/DTO/Options.php similarity index 67% rename from src/Infrastructure/Http/DTO/OptionsDTO.php rename to src/Infrastructure/Http/DTO/Options.php index 409ead0a..89a5131e 100644 --- a/src/Infrastructure/Http/DTO/OptionsDTO.php +++ b/src/Infrastructure/Http/DTO/Options.php @@ -2,12 +2,14 @@ namespace Logeecom\Infrastructure\Http\DTO; +use Logeecom\Infrastructure\Data\DataTransferObject; + /** - * Class OptionsDTO. Represents HTTP options set for Request by HttpClient. + * Class Options. Represents HTTP options set for Request by HttpClient. * * @package Logeecom\Infrastructure\Http\DTO */ -class OptionsDTO +class Options extends DataTransferObject { /** * Name of the option. @@ -23,7 +25,7 @@ class OptionsDTO private $value; /** - * OptionsDTO constructor. + * Options constructor. * * @param string $name Name of the option. * @param string $value Value of the option. @@ -68,31 +70,14 @@ public function toArray() } /** - * Transforms raw array data to OptionsDTO. + * Transforms raw array data to Options. * * @param array $raw Raw array data. * - * @return OptionsDTO Transformed object. + * @return Options Transformed object. */ public static function fromArray(array $raw) { return new static($raw['name'], $raw['value']); } - - /** - * Transforms batch of raw array data to an array of DTOs. - * - * @param array $batchRaw Raw array data. - * - * @return static[] Array of transformed DTOs. - */ - public static function fromArrayBatch(array $batchRaw) - { - $results = array(); - foreach ($batchRaw as $item) { - $results[] = static::fromArray($item); - } - - return $results; - } } diff --git a/src/Infrastructure/Http/HttpClient.php b/src/Infrastructure/Http/HttpClient.php index 94238c9d..05e959ee 100644 --- a/src/Infrastructure/Http/HttpClient.php +++ b/src/Infrastructure/Http/HttpClient.php @@ -3,7 +3,7 @@ namespace Logeecom\Infrastructure\Http; use Logeecom\Infrastructure\Configuration\Configuration; -use Logeecom\Infrastructure\Http\DTO\OptionsDTO; +use Logeecom\Infrastructure\Http\DTO\Options; use Logeecom\Infrastructure\Http\Exceptions\HttpCommunicationException; use Logeecom\Infrastructure\Logger\Logger; use Logeecom\Infrastructure\ServiceRegister; @@ -116,9 +116,10 @@ public function request($method, $url, $headers = array(), $body = '') * * @param string $method HTTP method (GET, POST, PUT, DELETE etc.) * @param string $url Request URL. Full URL where request should be sent. - * @param array|null $headers [Optional] Request headers to send. Key as header name and value as header content. - * @param string $body [Optional] Request payload. String data to send as HTTP request payload. Default value for - * a request body is '1' to ensure a minimal request data in a case of POST, PUT, PATCH methods. + * @param array|null $headers Request headers to send. Key as header name and value as header content. Optional. + * @param string $body Request payload. String data to send as HTTP request payload. Optional. Default value for + * request body is '1' to ensure minimal request data in case of POST, PUT, PATCH methods. + * */ public function requestAsync($method, $url, $headers = array(), $body = '1') { @@ -193,11 +194,9 @@ abstract protected function sendHttpRequest($method, $url, $headers = array(), $ * * @param string $method HTTP method (GET, POST, PUT, DELETE etc.) * @param string $url Request URL. Full URL where request should be sent. - * @param array|null $headers [Optional] Request headers to send. Key as header name and value as header content. - * @param string $body [Optional] Request payload. String data to send as HTTP request payload. Default value for - * request body is '1' to ensure minimal request data in case of POST, PUT, PATCH methods. This will ensure - * that we have the upload progress and enable the async request termination as soon as the upload is finished - * without waiting for a response (without downloading a body or relaying on a fixed request timeout). + * @param array|null $headers Request headers to send. Key as header name and value as header content. Optional. + * @param string $body Request payload. String data to send as HTTP request payload. Optional. Default value for + * request body is '1' to ensure minimal request data in case of POST, PUT, PATCH methods. */ abstract protected function sendHttpRequestAsync($method, $url, $headers = array(), $body = '1'); @@ -208,7 +207,9 @@ abstract protected function sendHttpRequestAsync($method, $url, $headers = array * @param string $url Request URL. * * @return array - * Array of additional options combinations. Each array item should be an array of OptionsDTO instances. + * Array of additional options combinations. Each array item should be an array of Options instances. + * + * @noinspection PhpUnusedParameterInspection */ protected function getAutoConfigurationOptionsCombinations($method, $url) { @@ -221,7 +222,7 @@ protected function getAutoConfigurationOptionsCombinations($method, $url) * Save additional options for request. * * @param string $domain A domain for which to set configuration options. - * @param OptionsDTO[] $options Additional options to add to HTTP request. + * @param Options[] $options Additional options to add to HTTP request. */ protected function setAdditionalOptions($domain, $options) { @@ -273,7 +274,6 @@ protected function getAdditionalOptions($domain) private function isRequestSuccessful($method, $url, $headers = array(), $body = '') { try { - /** @var HttpResponse $response */ $response = $this->request($method, $url, $headers, $body); } catch (HttpCommunicationException $ex) { $response = null; diff --git a/src/Infrastructure/Http/HttpResponse.php b/src/Infrastructure/Http/HttpResponse.php index aa87205d..a3ac3690 100644 --- a/src/Infrastructure/Http/HttpResponse.php +++ b/src/Infrastructure/Http/HttpResponse.php @@ -69,11 +69,13 @@ public function getBody() /** * Returns json decoded response body. * - * @return mixed Response body decoded as json decode. + * @return array Response body decoded as json decode. */ - public function decodeBodyAsJson() + public function decodeBodyToArray() { - return json_decode($this->body, true); + $result = json_decode($this->body, true); + + return !empty($result) ? $result : array(); } /** diff --git a/src/Infrastructure/ORM/Configuration/Index.php b/src/Infrastructure/ORM/Configuration/Index.php index ee86dc68..bcec719b 100644 --- a/src/Infrastructure/ORM/Configuration/Index.php +++ b/src/Infrastructure/ORM/Configuration/Index.php @@ -2,6 +2,8 @@ namespace Logeecom\Infrastructure\ORM\Configuration; +use InvalidArgumentException; + /** * Represents an indexed column in database table. * @@ -51,7 +53,7 @@ class Index public function __construct($type, $property) { if (!in_array($type, array(self::BOOLEAN, self::DATETIME, self::DOUBLE, self::INTEGER, self::STRING), true)) { - throw new \InvalidArgumentException("Invalid index type given: $type."); + throw new InvalidArgumentException("Invalid index type given: $type."); } $this->type = $type; diff --git a/src/Infrastructure/ORM/Entity.php b/src/Infrastructure/ORM/Entity.php index a503ae05..cc11d5b8 100644 --- a/src/Infrastructure/ORM/Entity.php +++ b/src/Infrastructure/ORM/Entity.php @@ -2,6 +2,8 @@ namespace Logeecom\Infrastructure\ORM; +use InvalidArgumentException; +use Logeecom\Infrastructure\Data\DataTransferObject; use Logeecom\Infrastructure\ORM\Configuration\EntityConfiguration; /** @@ -9,7 +11,7 @@ * * @package Logeecom\Infrastructure\ORM\Entities */ -abstract class Entity +abstract class Entity extends DataTransferObject { /** * Fully qualified name of this class. @@ -130,7 +132,7 @@ public function getIndexValue($indexKey) return $this->$indexKey; } - throw new \InvalidArgumentException('Neither field not getter found for index "' . $indexKey . '".'); + throw new InvalidArgumentException('Neither field not getter found for index "' . $indexKey . '".'); } /** diff --git a/src/Infrastructure/ORM/Interfaces/QueueItemRepository.php b/src/Infrastructure/ORM/Interfaces/QueueItemRepository.php index 977b2c77..3065e884 100644 --- a/src/Infrastructure/ORM/Interfaces/QueueItemRepository.php +++ b/src/Infrastructure/ORM/Interfaces/QueueItemRepository.php @@ -16,15 +16,18 @@ interface QueueItemRepository extends RepositoryInterface { /** - * Finds list of earliest queued queue items per queue. Following list of criteria for searching must be satisfied: + * Finds list of earliest queued queue items per queue for given priority. + * Following list of criteria for searching must be satisfied: * - Queue must be without already running queue items * - For one queue only one (oldest queued) item should be returned + * - Only queue items with given priority can be retrieved. * + * @param int $priority Queue item priority priority. * @param int $limit Result set limit. By default max 10 earliest queue items will be returned * * @return QueueItem[] Found queue item list */ - public function findOldestQueuedItems($limit = 10); + public function findOldestQueuedItems($priority, $limit = 10); /** * Creates or updates given queue item. If queue item id is not set, new queue item will be created otherwise diff --git a/src/Infrastructure/ORM/QueryFilter/QueryCondition.php b/src/Infrastructure/ORM/QueryFilter/QueryCondition.php index e0ea6e8d..5c742afe 100644 --- a/src/Infrastructure/ORM/QueryFilter/QueryCondition.php +++ b/src/Infrastructure/ORM/QueryFilter/QueryCondition.php @@ -2,6 +2,8 @@ namespace Logeecom\Infrastructure\ORM\QueryFilter; +use DateTime; + /** * Class Condition * @package Logeecom\Infrastructure\ORM\QueryFilter @@ -45,7 +47,7 @@ public function __construct($chainOperator, $column, $operator, $value) $this->value = $value; $this->valueType = gettype($value); - if ($this->valueType === 'object' && $value instanceof \DateTime) { + if ($this->valueType === 'object' && $value instanceof DateTime) { $this->valueType = 'dateTime'; } } diff --git a/src/Infrastructure/ORM/RepositoryRegistry.php b/src/Infrastructure/ORM/RepositoryRegistry.php index 5212b632..32ab2ab5 100644 --- a/src/Infrastructure/ORM/RepositoryRegistry.php +++ b/src/Infrastructure/ORM/RepositoryRegistry.php @@ -35,19 +35,19 @@ class RepositoryRegistry */ public static function getRepository($entityClass) { - if (!self::isRegistered($entityClass)) { + if (!static::isRegistered($entityClass)) { throw new RepositoryNotRegisteredException("Repository for entity $entityClass not found or registered."); } - if (!array_key_exists($entityClass, self::$instantiated)) { - $repositoryClass = self::$repositories[$entityClass]; + if (!array_key_exists($entityClass, static::$instantiated)) { + $repositoryClass = static::$repositories[$entityClass]; /** @var RepositoryInterface $repository */ $repository = new $repositoryClass(); $repository->setEntityClass($entityClass); - self::$instantiated[$entityClass] = $repository; + static::$instantiated[$entityClass] = $repository; } - return self::$instantiated[$entityClass]; + return static::$instantiated[$entityClass]; } /** @@ -64,8 +64,8 @@ public static function registerRepository($entityClass, $repositoryClass) throw new RepositoryClassException("Class $repositoryClass is not implementation of RepositoryInterface."); } - unset(self::$instantiated[$entityClass]); - self::$repositories[$entityClass] = $repositoryClass; + unset(static::$instantiated[$entityClass]); + static::$repositories[$entityClass] = $repositoryClass; } /** @@ -77,7 +77,7 @@ public static function registerRepository($entityClass, $repositoryClass) */ public static function isRegistered($entityClass) { - return isset(self::$repositories[$entityClass]); + return isset(static::$repositories[$entityClass]); } /** @@ -91,7 +91,7 @@ public static function isRegistered($entityClass) public static function getQueueItemRepository() { /** @var QueueItemRepository $repository */ - $repository = self::getRepository(QueueItem::getClassName()); + $repository = static::getRepository(QueueItem::getClassName()); if (!($repository instanceof QueueItemRepository)) { throw new RepositoryClassException('Instance class is not implementation of QueueItemRepository'); } diff --git a/src/Infrastructure/ORM/Utility/IndexHelper.php b/src/Infrastructure/ORM/Utility/IndexHelper.php index 193eaf13..9106d313 100644 --- a/src/Infrastructure/ORM/Utility/IndexHelper.php +++ b/src/Infrastructure/ORM/Utility/IndexHelper.php @@ -2,6 +2,7 @@ namespace Logeecom\Infrastructure\ORM\Utility; +use DateTime; use Logeecom\Infrastructure\ORM\Entity; /** @@ -64,7 +65,7 @@ public static function castFieldValue($value, $type) return $value; } - if ($type === 'dateTime' && $value instanceof \DateTime) { + if ($type === 'dateTime' && $value instanceof DateTime) { return (string)$value->getTimestamp(); } diff --git a/src/Infrastructure/TaskExecution/AsyncBatchStarter.php b/src/Infrastructure/TaskExecution/AsyncBatchStarter.php new file mode 100644 index 00000000..3125aaee --- /dev/null +++ b/src/Infrastructure/TaskExecution/AsyncBatchStarter.php @@ -0,0 +1,265 @@ +batchSize, $this->subBatches, $this->runners, $this->addIndex)); + } + + /** + * @inheritDoc + */ + public function unserialize($serialized) + { + list( + $this->batchSize, $this->subBatches, $this->runners, $this->addIndex + ) = + Serializer::unserialize($serialized); + } + + /** + * @inheritDoc + */ + public static function fromArray(array $data) + { + $runners = array(); + $subBatches = array(); + foreach ($data['runners'] as $runner) { + $runners[] = Serializer::unserialize($runner); + } + + foreach ($data['subBatches'] as $subBatch) { + $subBatches[] = Serializer::unserialize($subBatch); + } + + $instance = new self($data['batchSize'], $runners); + $instance->subBatches = $subBatches; + $instance->addIndex = $data['addIndex']; + + return $instance; + } + + /** + * @inheritDoc + */ + public function toArray() + { + $runners = array(); + $subBatches = array(); + foreach ($this->runners as $runner) { + $runners[] = Serializer::serialize($runner); + } + + foreach ($this->subBatches as $subBatch) { + $subBatches[] = Serializer::serialize($subBatch); + } + + return array( + 'batchSize' => $this->batchSize, + 'subBatches' => $subBatches, + 'runners' => $runners, + 'addIndex' => $this->addIndex, + ); + } + + /** + * AsyncBatchStarter constructor. + * + * @param int $batchSize + * @param Runnable[] $runners + */ + public function __construct($batchSize, array $runners = array()) + { + $this->batchSize = $batchSize; + foreach ($runners as $runner) { + $this->addRunner($runner); + } + } + + /** + * Add runnable to the batch + * + * @param Runnable $runner + */ + public function addRunner(Runnable $runner) + { + if ($this->isCapacityFull()) { + $this->subBatches[$this->addIndex]->addRunner($runner); + $this->addIndex = ($this->addIndex + 1) % $this->batchSize; + + return; + } + + if ($this->isRunnersCapacityFull()) { + $this->subBatches[] = new self($this->batchSize, $this->runners); + $this->runners = array(); + } + + $this->runners[] = $runner; + } + + /** + * @inheritDoc + * + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\ProcessStarterSaveException + */ + public function run() + { + foreach ($this->subBatches as $subBatch) { + $this->getAsyncProcessStarter()->start($subBatch); + } + + foreach ($this->runners as $runner) { + $this->getAsyncProcessStarter()->start($runner); + } + } + + /** + * Returns max number of nested sub-batch levels. No sub-batches will return 0, one sub-batch 1, sub-batch with + * sub-batch 2.... + * + * @return int Max number of nested sub-batch levels + */ + public function getMaxNestingLevels() + { + if (empty($this->subBatches)) { + return 0; + } + + $maxLevel = 0; + foreach ($this->subBatches as $subBatch) { + $subBatchMaxLevel = $subBatch->getMaxNestingLevels(); + if ($maxLevel < $subBatchMaxLevel) { + $maxLevel = $subBatchMaxLevel; + } + } + + return $maxLevel + 1; + } + + /** + * Calculates time required for whole batch with its sub-batches to run. Wait time calculation si based on HTTP + * request duration provided as method argument + * + * @param float $requestDuration Expected HTTP request duration in microseconds. + * + * @return float Wait period in micro seconds that is required for whole batch (with sub-batches) to run + */ + public function getWaitTime($requestDuration) + { + // Without sub-batches all requests are started as soon as run method is done + if (empty($this->subBatches)) { + return 0; + } + + $subBatchWaitTime = $this->batchSize * $this->getMaxNestingLevels() * $requestDuration; + $runnersStartupTime = count($this->runners) * $requestDuration; + + return $subBatchWaitTime - $runnersStartupTime; + } + + /** + * Returns string representation of the class. + * + * @return string String representation. + */ + public function __toString() + { + $out = implode(', ', $this->subBatches); + $countOfRunners = count($this->runners); + for ($i = 0; $i < $countOfRunners; $i++) { + $out .= empty($out) ? 'R' : ', R'; + } + + return "B({$out})"; + } + + /** + * @return bool + * True if current batch cant take any more runners nor create any more sub-batches itself; False otherwise + */ + protected function isCapacityFull() + { + return $this->isRunnersCapacityFull() && $this->isSubBatchCapacityFull(); + } + + /** + * @return bool + * True if current batch cant create any more sub-batches itself; False otherwise + */ + protected function isSubBatchCapacityFull() + { + return count($this->subBatches) >= $this->batchSize; + } + + /** + * @return bool + * True if current batch cant take any more runners itself; False otherwise + */ + protected function isRunnersCapacityFull() + { + return count($this->runners) >= $this->batchSize; + } + + /** + * Gets instance of async process starter. + * + * @return AsyncProcessService + * Instance of async process starter. + */ + protected function getAsyncProcessStarter() + { + if ($this->asyncProcessStarter === null) { + $this->asyncProcessStarter = ServiceRegister::getService(AsyncProcessService::CLASS_NAME); + } + + return $this->asyncProcessStarter; + } +} \ No newline at end of file diff --git a/src/Infrastructure/TaskExecution/Interfaces/Priority.php b/src/Infrastructure/TaskExecution/Interfaces/Priority.php new file mode 100644 index 00000000..58d51ff3 --- /dev/null +++ b/src/Infrastructure/TaskExecution/Interfaces/Priority.php @@ -0,0 +1,24 @@ +lastExecutionProgressBasePoints = $lastExecutionProgressBasePoints; @@ -364,7 +374,7 @@ public function getProgressBasePoints() public function setProgressBasePoints($progressBasePoints) { if (!is_int($progressBasePoints) || $progressBasePoints < 0 || 10000 < $progressBasePoints) { - throw new \InvalidArgumentException('Progress percentage must be value between 0 and 100.'); + throw new InvalidArgumentException('Progress percentage must be value between 0 and 100.'); } $this->progressBasePoints = $progressBasePoints; @@ -663,6 +673,30 @@ public function getLastExecutionProgress() return $this->lastExecutionProgressBasePoints; } + /** + * Retrieves queue item execution priority. + * + * @return int QueueItem execution priority. + */ + public function getPriority() + { + return $this->priority ?: Priority::NORMAL; + } + + /** + * Sets queue item execution priority,. + * + * @param int $priority QueueItem execution priority. + */ + public function setPriority($priority) + { + if (!in_array($priority, static::getAvailablePriorities(), true)) { + throw new InvalidArgumentException("Priority {$priority} is not supported."); + } + + $this->priority = $priority; + } + /** * Reconfigures underlying task. * @@ -693,7 +727,8 @@ public function getConfig() ->addStringIndex('context') ->addDateTimeIndex('queueTime') ->addIntegerIndex('lastExecutionProgress') - ->addIntegerIndex('lastUpdateTimestamp'); + ->addIntegerIndex('lastUpdateTimestamp') + ->addIntegerIndex('priority'); return new EntityConfiguration($indexMap, 'QueueItem'); } @@ -715,6 +750,7 @@ public function toArray() $result['finishTime'] = $this->timeProvider->serializeDate($this->finishTime); $result['failTime'] = $this->timeProvider->serializeDate($this->failTime); $result['earliestStartTime'] = $this->timeProvider->serializeDate($this->earliestStartTime); + $result['priority'] = $this->getPriority(); return $result; } @@ -737,6 +773,16 @@ public function inflate(array $data) $this->earliestStartTime = $this->timeProvider->deserializeDateString($data['earliestStartTime']); } + /** + * Defines available priorities. + * + * @return array + */ + public static function getAvailablePriorities() + { + return array(Priority::LOW, Priority::NORMAL, Priority::HIGH); + } + /** * Gets timestamp of datetime. * @@ -745,7 +791,7 @@ public function inflate(array $data) * @return int|null * Timestamp of provided datetime or null if time is not defined. */ - protected function getTimestamp(\DateTime $time = null) + protected function getTimestamp(DateTime $time = null) { return $time !== null ? $time->getTimestamp() : null; } diff --git a/src/Infrastructure/TaskExecution/QueueService.php b/src/Infrastructure/TaskExecution/QueueService.php index 8536f873..6c05fcfd 100644 --- a/src/Infrastructure/TaskExecution/QueueService.php +++ b/src/Infrastructure/TaskExecution/QueueService.php @@ -2,6 +2,7 @@ namespace Logeecom\Infrastructure\TaskExecution; +use BadMethodCallException; use Logeecom\Infrastructure\Configuration\Configuration; use Logeecom\Infrastructure\ORM\Interfaces\QueueItemRepository; use Logeecom\Infrastructure\ORM\QueryFilter\QueryFilter; @@ -11,6 +12,7 @@ use Logeecom\Infrastructure\TaskExecution\Events\QueueStatusChangedEvent; use Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemSaveException; use Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException; +use Logeecom\Infrastructure\TaskExecution\Interfaces\Priority; use Logeecom\Infrastructure\TaskExecution\Interfaces\TaskRunnerWakeup; use Logeecom\Infrastructure\Utility\Events\EventBus; use Logeecom\Infrastructure\Utility\TimeProvider; @@ -64,18 +66,21 @@ class QueueService * integration) context based on account id should be provided. Failing to do this will result in global task * context and unpredictable task execution. * + * @param int | null $priority Null priority falls back to Priority::NORMAL + * * @return \Logeecom\Infrastructure\TaskExecution\QueueItem Created queue item. * - * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException - * When queue storage fails to save the item. + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException When queue storage + * fails to save the item. */ - public function enqueue($queueName, Task $task, $context = '') + public function enqueue($queueName, Task $task, $context = '', $priority = null) { $queueItem = new QueueItem($task); $queueItem->setStatus(QueueItem::QUEUED); $queueItem->setQueueName($queueName); $queueItem->setContext($context); $queueItem->setQueueTimestamp($this->getTimeProvider()->getCurrentLocalTime()->getTimestamp()); + $queueItem->setPriority($priority ?: ($task->getPriority() ?: Priority::NORMAL)); $this->save($queueItem, array(), true, QueueItem::CREATED); @@ -258,7 +263,7 @@ public function abort(QueueItem $queueItem, $abortDescription) public function updateProgress(QueueItem $queueItem, $progress) { if ($queueItem->getStatus() !== QueueItem::IN_PROGRESS) { - throw new \BadMethodCallException('Progress reported for not started queue item.'); + throw new BadMethodCallException('Progress reported for not started queue item.'); } $lastExecutionProgress = $queueItem->getLastExecutionProgressBasePoints(); @@ -363,6 +368,7 @@ public function findRunningItems() /** * Finds list of earliest queued queue items per queue. * Only queues that doesn't have running tasks are taken in consideration. + * Returned queue items are ordered in the descending priority. * * @param int $limit Result set limit. By default max 10 earliest queue items will be returned. * @@ -370,7 +376,21 @@ public function findRunningItems() */ public function findOldestQueuedItems($limit = 10) { - return $this->getStorage()->findOldestQueuedItems($limit); + $result = array(); + $currentLimit = $limit; + + foreach (QueueItem::getAvailablePriorities() as $priority) { + $batch = $this->getStorage()->findOldestQueuedItems($priority, $currentLimit); + $result[] = $batch; + + if (($currentLimit -= count($batch)) <= 0) { + break; + } + } + + $result = !empty($result) ? call_user_func_array('array_merge', $result) : $result; + + return array_slice($result, 0, $limit); } /** @@ -505,7 +525,7 @@ private function getConfigService() */ private function throwIllegalTransitionException($fromStatus, $toStatus) { - throw new \BadMethodCallException( + throw new BadMethodCallException( sprintf( 'Illegal queue item state transition from "%s" to "%s"', $fromStatus, diff --git a/src/Infrastructure/TaskExecution/Task.php b/src/Infrastructure/TaskExecution/Task.php index cc2810e8..6f09304c 100644 --- a/src/Infrastructure/TaskExecution/Task.php +++ b/src/Infrastructure/TaskExecution/Task.php @@ -6,6 +6,7 @@ use Logeecom\Infrastructure\Serializer\Interfaces\Serializable; use Logeecom\Infrastructure\Serializer\Serializer; use Logeecom\Infrastructure\ServiceRegister; +use Logeecom\Infrastructure\TaskExecution\Interfaces\Priority; use Logeecom\Infrastructure\TaskExecution\TaskEvents\AliveAnnouncedTaskEvent; use Logeecom\Infrastructure\TaskExecution\TaskEvents\TaskProgressEvent; use Logeecom\Infrastructure\Utility\Events\EventEmitter; @@ -68,6 +69,16 @@ public function unserialize($serialized) // this task doesn't have any properties which needs to encapsulate. } + /** + * Retrieves task priority. + * + * @return int Task priority. + */ + public function getPriority() + { + return Priority::NORMAL; + } + /** * Reports task progress by emitting @see TaskProgressEvent and defers next @see AliveAnnouncedTaskEvent. * diff --git a/src/Infrastructure/TaskExecution/TaskRunner.php b/src/Infrastructure/TaskExecution/TaskRunner.php index 05b3bda2..d95edec3 100644 --- a/src/Infrastructure/TaskExecution/TaskRunner.php +++ b/src/Infrastructure/TaskExecution/TaskRunner.php @@ -2,10 +2,10 @@ namespace Logeecom\Infrastructure\TaskExecution; +use Exception; use Logeecom\Infrastructure\Configuration\Configuration; use Logeecom\Infrastructure\Logger\Logger; use Logeecom\Infrastructure\ServiceRegister; -use Logeecom\Infrastructure\TaskExecution\Interfaces\AsyncProcessService; use Logeecom\Infrastructure\TaskExecution\Interfaces\TaskRunnerStatusStorage; use Logeecom\Infrastructure\TaskExecution\Interfaces\TaskRunnerWakeup; use Logeecom\Infrastructure\Utility\TimeProvider; @@ -25,18 +25,16 @@ class TaskRunner * Automatic task runner wakeup delay in seconds */ const WAKEUP_DELAY = 5; + /** + * Defines minimal time in seconds between two consecutive alive since updates. + */ + const TASK_RUNNER_KEEP_ALIVE_PERIOD = 2; /** * Runner guid. * * @var string */ protected $guid; - /** - * Service. - * - * @var AsyncProcessStarterService - */ - private $asyncProcessStarter; /** * Service. * @@ -67,7 +65,18 @@ class TaskRunner * @var TaskRunnerWakeup */ private $taskWakeup; - + /** + * Defines when was the task runner alive since time step last updated at. + * + * @var int + */ + private $aliveSinceUpdatedAt = 0; + /** + * Sleep time in seconds with microsecond precision. + * + * @var float + */ + private $batchSleepTime = 0.0; /** * Sets task runner guid. * @@ -84,6 +93,8 @@ public function setGuid($guid) public function run() { try { + $this->keepAlive(); + $this->logDebug(array('Message' => 'Task runner: lifecycle started.')); if ($this->isCurrentRunnerAlive()) { @@ -91,10 +102,12 @@ public function run() $this->startOldestQueuedItems(); } + $this->keepAlive(); + $this->wakeup(); $this->logDebug(array('Message' => 'Task runner: lifecycle ended.')); - } catch (\Exception $ex) { + } catch (Exception $ex) { $this->logDebug( array( 'Message' => 'Fail to run task runner. Unexpected error occurred.', @@ -121,6 +134,8 @@ private function failOrRequeueExpiredTasks() return; } + $this->keepAlive(); + foreach ($runningItems as $runningItem) { if ($this->isItemExpired($runningItem) && $this->isCurrentRunnerAlive()) { $this->logMessageFor($runningItem, 'Task runner: Expired task detected.'); @@ -139,6 +154,8 @@ private function failOrRequeueExpiredTasks() ); } } + + $this->keepAlive(); } } @@ -151,10 +168,11 @@ private function failOrRequeueExpiredTasks() * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\ProcessStarterSaveException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\TaskRunnerStatusStorageUnavailableException - * @throws \Logeecom\Infrastructure\Http\Exceptions\HttpRequestException */ private function startOldestQueuedItems() { + $this->keepAlive(); + $this->logDebug(array('Message' => 'Task runner: available task detection started.')); // Calculate how many queue items can be started @@ -167,20 +185,47 @@ private function startOldestQueuedItems() return; } + $this->keepAlive(); + $items = $this->getQueue()->findOldestQueuedItems($numberOfAvailableSlots); + $this->keepAlive(); + if (!$this->isCurrentRunnerAlive()) { return; } + $asyncStarterBatchSize = $this->getConfigurationService()->getAsyncStarterBatchSize(); + $batchStarter = new AsyncBatchStarter($asyncStarterBatchSize); foreach ($items as $item) { - if (!$this->isCurrentRunnerAlive()) { - return; - } + $this->logMessageFor($item, 'Task runner: Adding task to a batch starter for async execution.'); + $batchStarter->addRunner(new QueueItemStarter($item->getId())); + } + + $this->keepAlive(); - $this->logMessageFor($item, 'Task runner: Starting async task execution.'); - $this->getAsyncProcessStarter()->start(new QueueItemStarter($item->getId())); + if (!$this->isCurrentRunnerAlive()) { + return; } + + $this->logDebug(array('Message' => 'Task runner: Starting batch starter execution.')); + $startTime = $this->getTimeProvider()->getMicroTimestamp(); + $batchStarter->run(); + $endTime = $this->getTimeProvider()->getMicroTimestamp(); + + $this->keepAlive(); + + $averageRequestTime = ($endTime - $startTime) / $asyncStarterBatchSize; + $this->batchSleepTime = $batchStarter->getWaitTime($averageRequestTime); + + $this->logDebug( + array( + 'Message' => 'Task runner: Batch starter execution finished.', + 'ExecutionTime' => ($endTime - $startTime) . 's', + 'AverageRequestTime' => $averageRequestTime . 's', + 'StartedItems' => count($items), + ) + ); } /** @@ -192,7 +237,11 @@ private function startOldestQueuedItems() private function wakeup() { $this->logDebug(array('Message' => 'Task runner: starting self deactivation.')); - $this->getTimeProvider()->sleep($this->getWakeupDelay()); + + for ($i = 0; $i < $this->getWakeupDelay(); $i++) { + $this->getTimeProvider()->sleep(1); + $this->keepAlive(); + } $this->getRunnerStorage()->setStatus(TaskRunnerStatus::createNullStatus()); @@ -200,6 +249,23 @@ private function wakeup() $this->getTaskWakeup()->wakeup(); } + /** + * Updates alive since time stamp of the task runner. + * + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\TaskRunnerStatusStorageUnavailableException + */ + private function keepAlive() + { + $currentTime = $this->getTimeProvider()->getCurrentLocalTime()->getTimestamp(); + if (($currentTime - $this->aliveSinceUpdatedAt) < self::TASK_RUNNER_KEEP_ALIVE_PERIOD) { + + return; + } + + $this->getConfigurationService()->setTaskRunnerStatus($this->guid, $currentTime); + $this->aliveSinceUpdatedAt = $currentTime; + } + /** * Checks whether current runner is alive. * @@ -256,23 +322,9 @@ private function getItemDescription(QueueItem $item) } /** - * Gets @see AsyncProcessStarterService service instance. + * Gets @return QueueService Queue service instance. + * @see QueueService service instance. * - * @return AsyncProcessStarterService Class instance. - */ - private function getAsyncProcessStarter() - { - if ($this->asyncProcessStarter === null) { - $this->asyncProcessStarter = ServiceRegister::getService(AsyncProcessService::CLASS_NAME); - } - - return $this->asyncProcessStarter; - } - - /** - * Gets @see QueueService service instance. - * - * @return QueueService Queue service instance. */ private function getQueue() { @@ -284,9 +336,9 @@ private function getQueue() } /** - * Gets @see TaskRunnerStatusStorageInterface service instance. + * Gets @return TaskRunnerStatusStorage Service instance. + * @see TaskRunnerStatusStorageInterface service instance. * - * @return TaskRunnerStatusStorage Service instance. */ private function getRunnerStorage() { @@ -298,9 +350,9 @@ private function getRunnerStorage() } /** - * Gets @see Configuration service instance. + * Gets @return Configuration Service instance. + * @see Configuration service instance. * - * @return Configuration Service instance. */ private function getConfigurationService() { @@ -312,9 +364,9 @@ private function getConfigurationService() } /** - * Gets @see TimeProvider instance. + * Gets @return TimeProvider Service instance. + * @see TimeProvider instance. * - * @return TimeProvider Service instance. */ private function getTimeProvider() { @@ -326,9 +378,9 @@ private function getTimeProvider() } /** - * Gets @see TaskRunnerWakeupInterface service instance. + * Gets @return TaskRunnerWakeup Service instance. + * @see TaskRunnerWakeupInterface service instance. * - * @return TaskRunnerWakeup Service instance. */ private function getTaskWakeup() { @@ -348,7 +400,9 @@ private function getWakeupDelay() { $configurationValue = $this->getConfigurationService()->getTaskRunnerWakeupDelay(); - return $configurationValue !== null ? $configurationValue : self::WAKEUP_DELAY; + $minimalSleepTime = $configurationValue !== null ? $configurationValue : self::WAKEUP_DELAY; + + return $minimalSleepTime + ceil($this->batchSleepTime); } /** diff --git a/src/Infrastructure/TaskExecution/TaskRunnerStatus.php b/src/Infrastructure/TaskExecution/TaskRunnerStatus.php index 37b67749..9ab9d356 100644 --- a/src/Infrastructure/TaskExecution/TaskRunnerStatus.php +++ b/src/Infrastructure/TaskExecution/TaskRunnerStatus.php @@ -15,7 +15,7 @@ class TaskRunnerStatus /** * Maximal time allowed for runner instance to stay in alive (running) status in seconds */ - const MAX_ALIVE_TIME = 60; + const MAX_ALIVE_TIME = 15; /** * Identifier of task runner. * diff --git a/src/Infrastructure/Utility/TimeProvider.php b/src/Infrastructure/Utility/TimeProvider.php index 295d2b93..518edc33 100644 --- a/src/Infrastructure/Utility/TimeProvider.php +++ b/src/Infrastructure/Utility/TimeProvider.php @@ -2,6 +2,8 @@ namespace Logeecom\Infrastructure\Utility; +use DateTime; + /** * Class TimeProvider. * @@ -50,23 +52,21 @@ public static function getInstance() */ public function getCurrentLocalTime() { - /** @noinspection PhpUnhandledExceptionInspection */ - return new \DateTime(); + return new DateTime(); } /** * @noinspection PhpDocMissingThrowsInspection * - * Returns @see \DateTime object from timestamp. - * - * @param int $timestamp Timestamp in seconds. + * Returns @param int $timestamp Timestamp in seconds. * * @return \DateTime Object from timestamp. + * @see \DateTime object from timestamp. + * */ public function getDateTime($timestamp) { - /** @noinspection PhpUnhandledExceptionInspection */ - return new \DateTime("@{$timestamp}"); + return new DateTime("@{$timestamp}"); } /** @@ -76,7 +76,17 @@ public function getDateTime($timestamp) */ public function getMillisecondsTimestamp() { - return (int)round(microtime(true) * 1000); + return (int)round($this->getMicroTimestamp() * 1000); + } + + /** + * Returns current timestamp with microseconds (float value with microsecond precision) + * + * @return float Current timestamp as float value with microseconds. + */ + public function getMicroTimestamp() + { + return microtime(true); } /** @@ -103,7 +113,7 @@ public function deserializeDateString($dateTime, $format = null) return null; } - return \DateTime::createFromFormat($format ?: DATE_ATOM, $dateTime); + return DateTime::createFromFormat($format ?: DATE_ATOM, $dateTime); } /** @@ -114,7 +124,7 @@ public function deserializeDateString($dateTime, $format = null) * * @return string|null String serialized date. */ - public function serializeDate(\DateTime $dateTime = null, $format = null) + public function serializeDate(DateTime $dateTime = null, $format = null) { if ($dateTime === null) { return null; diff --git a/tests/BusinessLogic/Common/TestComponents/ORM/MemoryQueueItemReposiotoryWithConditionalDelete.php b/tests/BusinessLogic/Common/TestComponents/ORM/MemoryQueueItemReposiotoryWithConditionalDelete.php new file mode 100644 index 00000000..390bc601 --- /dev/null +++ b/tests/BusinessLogic/Common/TestComponents/ORM/MemoryQueueItemReposiotoryWithConditionalDelete.php @@ -0,0 +1,44 @@ +select($filter); + foreach ($toBeDeleted as $entity) { + $this->delete($entity); + } + } +} \ No newline at end of file diff --git a/tests/BusinessLogic/Common/TestComponents/TestShopConfiguration.php b/tests/BusinessLogic/Common/TestComponents/TestShopConfiguration.php index 89b7afa8..73a2374c 100644 --- a/tests/BusinessLogic/Common/TestComponents/TestShopConfiguration.php +++ b/tests/BusinessLogic/Common/TestComponents/TestShopConfiguration.php @@ -90,7 +90,7 @@ public function getSchedulerQueueName() */ public function getWebHookUrl() { - return 'https://example.com'; + return 'https://example.com?context=' . $this->getContext(); } /** diff --git a/tests/BusinessLogic/Country/CountryDtoTest.php b/tests/BusinessLogic/Country/CountryDtoTest.php index aaa6bc6c..77c6cb45 100644 --- a/tests/BusinessLogic/Country/CountryDtoTest.php +++ b/tests/BusinessLogic/Country/CountryDtoTest.php @@ -43,17 +43,17 @@ public function testFromArray() $countries = TestFrontDtoFactory::getFromBatch(Country::CLASS_KEY, $data); $this->assertCount(2, $countries); - $this->assertEquals('Germany', $countries[0]->name); - $this->assertEquals('DE', $countries[0]->code); - $this->assertEquals('10115', $countries[0]->postalCode); - $this->assertEquals('https://pro.packlink.de/registrieren', $countries[0]->registrationLink); - $this->assertEquals('DE', $countries[0]->platformCountry); + $this->assertEquals('Germany', $countries['de']->name); + $this->assertEquals('DE', $countries['de']->code); + $this->assertEquals('10115', $countries['de']->postalCode); + $this->assertEquals('https://pro.packlink.de/registrieren', $countries['de']->registrationLink); + $this->assertEquals('DE', $countries['de']->platformCountry); - $this->assertEquals('Netherlands', $countries[1]->name); - $this->assertEquals('NL', $countries[1]->code); - $this->assertEquals('1011', $countries[1]->postalCode); - $this->assertEquals('https://pro.packlink.com/register', $countries[1]->registrationLink); - $this->assertEquals('UN', $countries[1]->platformCountry); + $this->assertEquals('Netherlands', $countries['nl']->name); + $this->assertEquals('NL', $countries['nl']->code); + $this->assertEquals('1011', $countries['nl']->postalCode); + $this->assertEquals('https://pro.packlink.com/register', $countries['nl']->registrationLink); + $this->assertEquals('UN', $countries['nl']->platformCountry); } public function testToArray() diff --git a/tests/BusinessLogic/Dto/BaseDtoTest.php b/tests/BusinessLogic/Dto/BaseDtoTest.php index 24f0c1cc..363e35ba 100644 --- a/tests/BusinessLogic/Dto/BaseDtoTest.php +++ b/tests/BusinessLogic/Dto/BaseDtoTest.php @@ -32,7 +32,7 @@ protected function tearDown() } /** - * @expectedException \BadMethodCallException + * @expectedException \RuntimeException */ public function testFromArrayNotImplemented() { diff --git a/tests/BusinessLogic/Location/LocationServiceTest.php b/tests/BusinessLogic/Location/LocationServiceTest.php index 5f90edf6..a9102def 100644 --- a/tests/BusinessLogic/Location/LocationServiceTest.php +++ b/tests/BusinessLogic/Location/LocationServiceTest.php @@ -233,7 +233,7 @@ private function initShippingMethod($isDropOff = true) * @param float $basePrice * @param bool $isDropOff * - * @return \Packlink\BusinessLogic\DTO\BaseDto|\Packlink\BusinessLogic\Http\DTO\ShippingServiceDetails + * @return \Logeecom\Infrastructure\Data\DataTransferObject|\Packlink\BusinessLogic\Http\DTO\ShippingServiceDetails */ private function getShippingServiceDetails($id, $basePrice = 10.73, $isDropOff = true) { diff --git a/tests/BusinessLogic/Order/ShipmentDraftServiceTest.php b/tests/BusinessLogic/Order/ShipmentDraftServiceTest.php index e14ff62e..207e4ba4 100644 --- a/tests/BusinessLogic/Order/ShipmentDraftServiceTest.php +++ b/tests/BusinessLogic/Order/ShipmentDraftServiceTest.php @@ -153,6 +153,8 @@ function () { } ); + RepositoryRegistry::registerRepository(Schedule::getClassName(), MemoryRepository::getClassName()); + $this->shopConfig->setDefaultParcel(ParcelInfo::defaultParcel()); $this->shopConfig->setDefaultWarehouse(new TestWarehouse()); $this->shopConfig->setUserInfo(new User()); @@ -188,6 +190,42 @@ public function testCreateDraft() $this->assertEmpty($draftStatus->message); } + public function testSchedulesCreated() + { + // arrange + $this->shopConfig->setFirstShipmentDraftCreated(false); + + // act + $this->draftShipmentService->enqueueCreateShipmentDraftTask('test'); + + // assert + $schedules = $this->getScheduleRepository()->select(); + $this->assertCount(3, $schedules); + } + + public function testSchedulesNotCreated() + { + // arrange + $this->shopConfig->setFirstShipmentDraftCreated(true); + + // act + $this->draftShipmentService->enqueueCreateShipmentDraftTask('test'); + + // assert + $schedules = $this->getScheduleRepository()->select(); + $this->assertCount(0, $schedules); + } + + public function testSchedulesNotCreatedForCurrentUsers() + { + // act + $this->draftShipmentService->enqueueCreateShipmentDraftTask('test'); + + // assert + $schedules = $this->getScheduleRepository()->select(); + $this->assertCount(0, $schedules); + } + /** * Tests creating delayed task. * @@ -288,6 +326,11 @@ public function testStatusFailed() $this->assertEquals('Attempt 7: Error in task.', $draftStatus->message); } + private function getScheduleRepository() + { + return RepositoryRegistry::getRepository(Schedule::getClassName()); + } + /** * Returns responses for testing sending of shipment draft. * diff --git a/tests/BusinessLogic/ShippingMethod/ShippingMethodServiceCostsTest.php b/tests/BusinessLogic/ShippingMethod/ShippingMethodServiceCostsTest.php index 805913bc..47858477 100644 --- a/tests/BusinessLogic/ShippingMethod/ShippingMethodServiceCostsTest.php +++ b/tests/BusinessLogic/ShippingMethod/ShippingMethodServiceCostsTest.php @@ -867,7 +867,7 @@ protected function addShippingMethod($serviceId, $active = true) * @param bool $destinationDropOff * @param float $basePrice * - * @return \Packlink\BusinessLogic\DTO\BaseDto|\Packlink\BusinessLogic\Http\DTO\ShippingServiceDetails + * @return \Logeecom\Infrastructure\Data\DataTransferObject |\Packlink\BusinessLogic\Http\DTO\ShippingServiceDetails */ private function getShippingServiceDetails( $id, diff --git a/tests/BusinessLogic/ShippingMethod/ShippingMethodServiceTest.php b/tests/BusinessLogic/ShippingMethod/ShippingMethodServiceTest.php index 189693a2..b87fd607 100644 --- a/tests/BusinessLogic/ShippingMethod/ShippingMethodServiceTest.php +++ b/tests/BusinessLogic/ShippingMethod/ShippingMethodServiceTest.php @@ -361,7 +361,7 @@ public function testShippingServiceFromArray() /** * @param int $id * - * @return \Packlink\BusinessLogic\DTO\BaseDto|\Packlink\BusinessLogic\Http\DTO\ShippingService + * @return \Logeecom\Infrastructure\Data\DataTransferObject |\Packlink\BusinessLogic\Http\DTO\ShippingService */ private function getShippingService($id) { @@ -382,7 +382,7 @@ private function getShippingService($id) * @param int $id * @param string $carrierName * - * @return \Packlink\BusinessLogic\DTO\BaseDto|\Packlink\BusinessLogic\Http\DTO\ShippingServiceDetails + * @return \Logeecom\Infrastructure\Data\DataTransferObject|\Packlink\BusinessLogic\Http\DTO\ShippingServiceDetails */ private function getShippingServiceDetails($id, $carrierName = 'test carrier') { diff --git a/tests/BusinessLogic/Tasks/BatchTaskCleanupTaskExecuteTest.php b/tests/BusinessLogic/Tasks/BatchTaskCleanupTaskExecuteTest.php new file mode 100644 index 00000000..ceba7b52 --- /dev/null +++ b/tests/BusinessLogic/Tasks/BatchTaskCleanupTaskExecuteTest.php @@ -0,0 +1,206 @@ +repoSetup(); + } + + public function testTaskAgeCutOff() + { + // arrange + $task = new BatchTaskCleanupTask( + array(QueueItem::COMPLETED, QueueItem::IN_PROGRESS), + array('FooTask', 'BarTask') + ); + + // act + $task->execute(); + // arrange + $task = new BatchTaskCleanupTask( + array(QueueItem::COMPLETED, QueueItem::IN_PROGRESS), + array('FooTask', 'BarTask') + ); + + // act + $task->execute(); + + // assert + $tasks = $this->getQueueItemRepo()->select(); + $this->assertCount(1, $tasks); + $item = $tasks[0]; + $this->assertInstanceOf( + '\Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\FooTask', + $item->getTask() + ); + // assert + $tasks = $this->getQueueItemRepo()->select(); + $this->assertCount(1, $tasks); + $item = $tasks[0]; + $this->assertInstanceOf( + '\Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\FooTask', + $item->getTask() + ); + } + + public function testTasksOlderThanAgeCutOff() + { + // arrange + $task = new BatchTaskCleanupTask( + array(QueueItem::COMPLETED, QueueItem::IN_PROGRESS), + array('FooTask', 'BarTask') + ); + $this->getConfigService()->setMaxTaskAge(1); + + // act + $task->execute(); + + // assert + $tasks = $this->getQueueItemRepo()->select(); + $this->assertEmpty($tasks); + } + + public function testTasksYungerThenAgeCutOff() + { + // arrange + $task = new BatchTaskCleanupTask( + array(QueueItem::COMPLETED, QueueItem::IN_PROGRESS), + array('FooTask', 'BarTask') + ); + $this->getConfigService()->setMaxTaskAge(20); + + // act + $task->execute(); + + // assert + $tasks = $this->getQueueItemRepo()->select(); + $this->assertCount(2, $tasks); + } + + public function testRemoveSpecificTaskType() + { + // arrange + $task = new BatchTaskCleanupTask(array(QueueItem::COMPLETED, QueueItem::IN_PROGRESS), array('FooTask')); + $this->getConfigService()->setMaxTaskAge(1); + + // act + $task->execute(); + + // assert + $tasks = $this->getQueueItemRepo()->select(); + $this->assertCount(1, $tasks); + $item = $tasks[0]; + $this->assertInstanceOf( + '\Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\BarTask', + $item->getTask() + ); + } + + public function testTaskWithSpecificStatus() + { + // arrange + $task = new BatchTaskCleanupTask(array(QueueItem::COMPLETED), array('FooTask', 'BarTask')); + $this->getConfigService()->setMaxTaskAge(1); + + // act + $task->execute(); + + // assert + $tasks = $this->getQueueItemRepo()->select(); + $this->assertCount(1, $tasks); + $item = $tasks[0]; + $this->assertInstanceOf( + '\Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\BarTask', + $item->getTask() + ); + } + + public function testNoTaskTypesProvided() + { + // arrange + $task = new BatchTaskCleanupTask(array(QueueItem::COMPLETED, QueueItem::IN_PROGRESS)); + $this->getConfigService()->setMaxTaskAge(1); + + // act + $task->execute(); + + // assert + $tasks = $this->getQueueItemRepo()->select(); + $this->assertEmpty($tasks); + } + + /** + * @expectedException \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException + */ + public function testRepositoryDoesNotImplementConditionallyDeletes() + { + // arrange + RepositoryRegistry::registerRepository(QueueItem::getClassName(), MemoryQueueItemRepository::getClassName()); + $task = new BatchTaskCleanupTask(array(QueueItem::COMPLETED, QueueItem::IN_PROGRESS)); + + // act + $task->execute(); + } + + private function getTaskSet() + { + $time = new \DateTime(); + + return array( + $this->instantiateQueueItem(new FooTask(), $time->modify('-2 day'), QueueItem::COMPLETED), + $this->instantiateQueueItem(new BarTask(), $time->modify('-10 day'), QueueItem::IN_PROGRESS), + ); + } + + private function instantiateQueueItem(Task $task, \DateTime $queueTime, $status) + { + $item = new QueueItem($task); + $item->setQueueTimestamp($queueTime->getTimestamp()); + $item->setStatus($status); + + return $item; + } + + private function repoSetup() + { + $repo = $this->getQueueItemRepo(); + foreach ($this->getTaskSet() as $item) { + $repo->save($item); + } + } + + private function getQueueItemRepo() + { + return RepositoryRegistry::getRepository(QueueItem::getClassName()); + } + + /** + * @return \Packlink\BusinessLogic\Configuration | object + */ + private function getConfigService() + { + return ServiceRegister::getService(Configuration::CLASS_NAME); + } +} \ No newline at end of file diff --git a/tests/BusinessLogic/Tasks/BatchTaskCleanupTaskSerializationTest.php b/tests/BusinessLogic/Tasks/BatchTaskCleanupTaskSerializationTest.php new file mode 100644 index 00000000..19877fb7 --- /dev/null +++ b/tests/BusinessLogic/Tasks/BatchTaskCleanupTaskSerializationTest.php @@ -0,0 +1,98 @@ +assertEquals($task, $unserialized); + } + + public function testNativeSerializeNoTaskTypes() + { + // arrange + TestServiceRegister::registerService( + Serializer::CLASS_NAME, + function () { + return new NativeSerializer(); + } + ); + + $task = new BatchTaskCleanupTask(array('t1', 't2')); + $serialized = Serializer::serialize($task); + + // act + $unserialized = Serializer::unserialize($serialized); + + // assert + $this->assertEquals($task, $unserialized); + } + + public function testJsonSerialize() + { + // arrange + TestServiceRegister::registerService( + Serializer::CLASS_NAME, + function () { + return new JsonSerializer(); + } + ); + + $task = new BatchTaskCleanupTask(array('t1', 't2'), array('t3')); + $serialized = Serializer::serialize($task); + + // act + $unserialized = Serializer::unserialize($serialized); + + // assert + $this->assertEquals($task, $unserialized); + } + + public function testJsonSerializeNoTaskTypes() + { + // arrange + TestServiceRegister::registerService( + Serializer::CLASS_NAME, + function () { + return new JsonSerializer(); + } + ); + + $task = new BatchTaskCleanupTask(array('t1', 't2')); + $serialized = Serializer::serialize($task); + + // act + $unserialized = Serializer::unserialize($serialized); + + // assert + $this->assertEquals($task, $unserialized); + } +} \ No newline at end of file diff --git a/tests/BusinessLogic/User/UserAccountLoginTest.php b/tests/BusinessLogic/User/UserAccountLoginTest.php index aa1393c5..8229c6e9 100644 --- a/tests/BusinessLogic/User/UserAccountLoginTest.php +++ b/tests/BusinessLogic/User/UserAccountLoginTest.php @@ -15,12 +15,8 @@ use Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\TestQueueService; use Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\TestTaskRunnerWakeupService; use Logeecom\Tests\Infrastructure\Common\TestServiceRegister; -use Packlink\BusinessLogic\Scheduler\Models\DailySchedule; -use Packlink\BusinessLogic\Scheduler\Models\HourlySchedule; use Packlink\BusinessLogic\Scheduler\Models\Schedule; use Packlink\BusinessLogic\Scheduler\Models\WeeklySchedule; -use Packlink\BusinessLogic\Tasks\TaskCleanupTask; -use Packlink\BusinessLogic\Tasks\UpdateShipmentDataTask; use Packlink\BusinessLogic\Tasks\UpdateShippingServicesTask; use Packlink\BusinessLogic\User\UserAccountService; @@ -76,8 +72,6 @@ function () use ($taskRunnerStarter) { * * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoNotRegisteredException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoValidationException */ public function testEmptyApiKey() { @@ -96,8 +90,6 @@ public function testEmptyApiKey() * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoNotRegisteredException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoValidationException */ public function testLogin() { @@ -130,20 +122,12 @@ public function testLogin() /** @var Schedule[] $allSchedules */ $allSchedules = $scheduleRepository->select(); - $this->assertCount(5, $allSchedules); + $this->assertCount(1, $allSchedules); $expectedSchedules = array( WeeklySchedule::getClassName() => array( UpdateShippingServicesTask::getClassName() => 1, ), - HourlySchedule::getClassName() => array( - // 2 hourly schedules, starting every 30 minutes - UpdateShipmentDataTask::getClassName() => 2, - TaskCleanupTask::getClassName() => 1, - ), - DailySchedule::getClassName() => array( - UpdateShipmentDataTask::getClassName() => 1, - ), ); foreach ($allSchedules as $schedule) { @@ -171,8 +155,6 @@ public function testLogin() * * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoNotRegisteredException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoValidationException */ public function testLoginNoParcel() { @@ -196,8 +178,6 @@ public function testLoginNoParcel() * * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoNotRegisteredException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoValidationException */ public function testLoginNoWarehouse() { @@ -247,8 +227,6 @@ private function getMockResponses($parcel = true, $warehouse = true) /** * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoNotRegisteredException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoValidationException */ public function testLoginBadHttp() { @@ -303,8 +281,6 @@ private function getParcelMockResponses() * @throws \Logeecom\Infrastructure\Http\Exceptions\HttpAuthenticationException * @throws \Logeecom\Infrastructure\Http\Exceptions\HttpCommunicationException * @throws \Logeecom\Infrastructure\Http\Exceptions\HttpRequestException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoNotRegisteredException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoValidationException */ public function testSettingWarehouseInfo() { @@ -325,8 +301,6 @@ public function testSettingWarehouseInfo() * @throws \Logeecom\Infrastructure\Http\Exceptions\HttpAuthenticationException * @throws \Logeecom\Infrastructure\Http\Exceptions\HttpCommunicationException * @throws \Logeecom\Infrastructure\Http\Exceptions\HttpRequestException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoNotRegisteredException - * @throws \Packlink\BusinessLogic\DTO\Exceptions\FrontDtoValidationException */ public function testSettingUnsupportedWarehouse() { @@ -350,7 +324,7 @@ private function getWarehouseMockResponses() return array( new HttpResponse( 200, array(), file_get_contents(__DIR__ . '/../Common/ApiResponses/warehouses.json') - ) + ), ); } @@ -364,7 +338,7 @@ private function getUnsupportedWarehouseMockResponse() return array( new HttpResponse( 200, array(), file_get_contents(__DIR__ . '/../Common/ApiResponses/unsupportedWarehouse.json') - ) + ), ); } diff --git a/tests/Infrastructure/AutoTest/AutoTestServiceTest.php b/tests/Infrastructure/AutoTest/AutoTestServiceTest.php index 992fa057..c0b7f3b9 100644 --- a/tests/Infrastructure/AutoTest/AutoTestServiceTest.php +++ b/tests/Infrastructure/AutoTest/AutoTestServiceTest.php @@ -4,7 +4,7 @@ use Logeecom\Infrastructure\AutoTest\AutoTestLogger; use Logeecom\Infrastructure\AutoTest\AutoTestService; -use Logeecom\Infrastructure\Http\DTO\OptionsDTO; +use Logeecom\Infrastructure\Http\DTO\Options; use Logeecom\Infrastructure\Http\HttpClient; use Logeecom\Infrastructure\Logger\Interfaces\ShopLoggerAdapter; use Logeecom\Infrastructure\Logger\LogData; @@ -120,7 +120,7 @@ public function testStartAutoTestSuccess() { RepositoryRegistry::registerRepository(LogData::getClassName(), MemoryRepository::getClassName()); $domain = parse_url($this->shopConfig->getAsyncProcessUrl(''), PHP_URL_HOST); - $this->shopConfig->setHttpConfigurationOptions($domain, array(new OptionsDTO('test', 'value'))); + $this->shopConfig->setHttpConfigurationOptions($domain, array(new Options('test', 'value'))); $service = new AutoTestService(); $queueItemId = $service->startAutoTest(); diff --git a/tests/Infrastructure/Common/BaseInfrastructureTestWithServices.php b/tests/Infrastructure/Common/BaseInfrastructureTestWithServices.php index b92a8bd2..18cfb5d0 100644 --- a/tests/Infrastructure/Common/BaseInfrastructureTestWithServices.php +++ b/tests/Infrastructure/Common/BaseInfrastructureTestWithServices.php @@ -3,6 +3,7 @@ namespace Logeecom\Tests\Infrastructure\Common; +use DateTime; use Logeecom\Infrastructure\Configuration\ConfigEntity; use Logeecom\Infrastructure\Configuration\Configuration; use Logeecom\Infrastructure\Logger\Interfaces\DefaultLoggerAdapter; @@ -65,7 +66,7 @@ protected function setUp() $me = $this; $this->timeProvider = new TestTimeProvider(); - $this->timeProvider->setCurrentLocalTime(new \DateTime()); + $this->timeProvider->setCurrentLocalTime(new DateTime()); $this->shopConfig = new TestShopConfiguration(); $this->shopLogger = new TestShopLogger(); $this->defaultLogger = new TestDefaultLogger(); @@ -90,7 +91,7 @@ protected function setUp() }, Serializer::CLASS_NAME => function () use ($me) { return $me->serializer; - } + }, ) ); } diff --git a/tests/Infrastructure/Common/EntityData/QueueItems.json b/tests/Infrastructure/Common/EntityData/QueueItems.json index 8286ada5..d38fbe1d 100644 --- a/tests/Infrastructure/Common/EntityData/QueueItems.json +++ b/tests/Infrastructure/Common/EntityData/QueueItems.json @@ -13,7 +13,8 @@ "startTimestamp": 1513212413, "lastUpdateTimestamp": 1513212484, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "completed", @@ -29,7 +30,8 @@ "startTimestamp": 1494768739, "lastUpdateTimestamp": 1494768886, "finishTimestamp": 1494768971, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "failed", @@ -45,7 +47,8 @@ "startTimestamp": 1487457555, "lastUpdateTimestamp": 1487457729, "finishTimestamp": null, - "failTimestamp": 1487457746 + "failTimestamp": 1487457746, + "priority": 1 }, { "status": "completed", @@ -61,7 +64,8 @@ "startTimestamp": 1492662306, "lastUpdateTimestamp": 1492662341, "finishTimestamp": 1492662400, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "in_progress", @@ -77,7 +81,8 @@ "startTimestamp": 1524856866, "lastUpdateTimestamp": 1524856969, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -93,7 +98,8 @@ "startTimestamp": 1538849663, "lastUpdateTimestamp": 1538849787, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "in_progress", @@ -109,7 +115,8 @@ "startTimestamp": 1512603198, "lastUpdateTimestamp": 1512603264, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -125,7 +132,8 @@ "startTimestamp": 1493851234, "lastUpdateTimestamp": 1493851325, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -141,7 +149,8 @@ "startTimestamp": 1519853970, "lastUpdateTimestamp": 1519853974, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -157,7 +166,8 @@ "startTimestamp": 1518325648, "lastUpdateTimestamp": 1518325751, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "failed", @@ -173,7 +183,8 @@ "startTimestamp": 1485719633, "lastUpdateTimestamp": 1485719767, "finishTimestamp": null, - "failTimestamp": 1485719773 + "failTimestamp": 1485719773, + "priority": 1 }, { "status": "failed", @@ -189,7 +200,8 @@ "startTimestamp": 1504206574, "lastUpdateTimestamp": 1504206602, "finishTimestamp": null, - "failTimestamp": 1504206619 + "failTimestamp": 1504206619, + "priority": 1 }, { "status": "completed", @@ -205,7 +217,8 @@ "startTimestamp": 1523544632, "lastUpdateTimestamp": 1523544662, "finishTimestamp": 1523544686, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -221,7 +234,8 @@ "startTimestamp": 1521483184, "lastUpdateTimestamp": 1521483236, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -237,7 +251,8 @@ "startTimestamp": 1483927146, "lastUpdateTimestamp": 1483927271, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "failed", @@ -253,7 +268,8 @@ "startTimestamp": 1525193619, "lastUpdateTimestamp": 1525193744, "finishTimestamp": null, - "failTimestamp": 1525193765 + "failTimestamp": 1525193765, + "priority": 1 }, { "status": "completed", @@ -269,7 +285,8 @@ "startTimestamp": 1497802374, "lastUpdateTimestamp": 1497802502, "finishTimestamp": 1497802559, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "failed", @@ -285,7 +302,8 @@ "startTimestamp": 1516248095, "lastUpdateTimestamp": 1516248149, "finishTimestamp": null, - "failTimestamp": 1516248155 + "failTimestamp": 1516248155, + "priority": 1 }, { "status": "completed", @@ -301,7 +319,8 @@ "startTimestamp": 1516919779, "lastUpdateTimestamp": 1516919919, "finishTimestamp": 1516920004, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "queued", @@ -317,7 +336,8 @@ "startTimestamp": 1520640031, "lastUpdateTimestamp": 1520640196, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -333,7 +353,8 @@ "startTimestamp": 1521714303, "lastUpdateTimestamp": 1521714340, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -349,7 +370,8 @@ "startTimestamp": 1493729169, "lastUpdateTimestamp": 1493729261, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -365,7 +387,8 @@ "startTimestamp": 1506635318, "lastUpdateTimestamp": 1506635368, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "failed", @@ -381,7 +404,8 @@ "startTimestamp": 1535186273, "lastUpdateTimestamp": 1535186277, "finishTimestamp": null, - "failTimestamp": 1535186279 + "failTimestamp": 1535186279, + "priority": 1 }, { "status": "failed", @@ -397,7 +421,8 @@ "startTimestamp": 1517807912, "lastUpdateTimestamp": 1517807941, "finishTimestamp": null, - "failTimestamp": 1517807964 + "failTimestamp": 1517807964, + "priority": 1 }, { "status": "in_progress", @@ -413,7 +438,8 @@ "startTimestamp": 1497922251, "lastUpdateTimestamp": 1497922422, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "queued", @@ -429,7 +455,8 @@ "startTimestamp": 1531840716, "lastUpdateTimestamp": 1531840836, "finishTimestamp": 1531840883, - "failTimestamp": null + "failTimestamp": null, + "priority": 100 }, { "status": "failed", @@ -445,7 +472,8 @@ "startTimestamp": 1509214234, "lastUpdateTimestamp": 1509214389, "finishTimestamp": null, - "failTimestamp": 1509214408 + "failTimestamp": 1509214408, + "priority": 1 }, { "status": "queued", @@ -461,7 +489,8 @@ "startTimestamp": 1521467650, "lastUpdateTimestamp": 1521467689, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 100 }, { "status": "in_progress", @@ -477,7 +506,8 @@ "startTimestamp": 1535420435, "lastUpdateTimestamp": 1535420602, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "in_progress", @@ -493,7 +523,8 @@ "startTimestamp": 1538823136, "lastUpdateTimestamp": 1538823168, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "in_progress", @@ -509,7 +540,8 @@ "startTimestamp": 1500377352, "lastUpdateTimestamp": 1500377490, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "completed", @@ -525,7 +557,8 @@ "startTimestamp": 1505694001, "lastUpdateTimestamp": 1505694140, "finishTimestamp": 1505694161, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "failed", @@ -541,7 +574,8 @@ "startTimestamp": 1501569058, "lastUpdateTimestamp": 1501569094, "finishTimestamp": null, - "failTimestamp": 1501569103 + "failTimestamp": 1501569103, + "priority": 1 }, { "status": "failed", @@ -557,7 +591,8 @@ "startTimestamp": 1503745549, "lastUpdateTimestamp": 1503745576, "finishTimestamp": null, - "failTimestamp": 1503745599 + "failTimestamp": 1503745599, + "priority": 1 }, { "status": "completed", @@ -573,7 +608,8 @@ "startTimestamp": 1511030197, "lastUpdateTimestamp": 1511030365, "finishTimestamp": 1511030404, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "completed", @@ -589,7 +625,8 @@ "startTimestamp": 1509191960, "lastUpdateTimestamp": 1509192069, "finishTimestamp": 1509192149, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -605,7 +642,8 @@ "startTimestamp": 1534227111, "lastUpdateTimestamp": 1534227130, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -621,7 +659,8 @@ "startTimestamp": 1534591695, "lastUpdateTimestamp": 1534591793, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "completed", @@ -637,7 +676,8 @@ "startTimestamp": 1516640157, "lastUpdateTimestamp": 1516640240, "finishTimestamp": 1516640250, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "in_progress", @@ -653,7 +693,8 @@ "startTimestamp": 1519613996, "lastUpdateTimestamp": 1519614083, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "failed", @@ -669,7 +710,8 @@ "startTimestamp": 1530079195, "lastUpdateTimestamp": 1530079224, "finishTimestamp": null, - "failTimestamp": 1530079244 + "failTimestamp": 1530079244, + "priority": 1 }, { "status": "created", @@ -685,7 +727,8 @@ "startTimestamp": 1519780206, "lastUpdateTimestamp": 1519780313, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -701,7 +744,8 @@ "startTimestamp": 1528135962, "lastUpdateTimestamp": 1528136130, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "completed", @@ -717,7 +761,8 @@ "startTimestamp": 1533790499, "lastUpdateTimestamp": 1533790618, "finishTimestamp": 1533790645, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "created", @@ -733,7 +778,8 @@ "startTimestamp": 1506393222, "lastUpdateTimestamp": 1506393375, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "completed", @@ -749,7 +795,8 @@ "startTimestamp": 1511647704, "lastUpdateTimestamp": 1511647797, "finishTimestamp": 1511647816, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "failed", @@ -765,7 +812,8 @@ "startTimestamp": 1488176977, "lastUpdateTimestamp": 1488176989, "finishTimestamp": null, - "failTimestamp": 1488177006 + "failTimestamp": 1488177006, + "priority": 1 }, { "status": "completed", @@ -781,7 +829,8 @@ "startTimestamp": 1507951264, "lastUpdateTimestamp": 1507951302, "finishTimestamp": 1507951310, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 }, { "status": "queued", @@ -797,6 +846,7 @@ "startTimestamp": 1521857997, "lastUpdateTimestamp": 1521858067, "finishTimestamp": null, - "failTimestamp": null + "failTimestamp": null, + "priority": 1 } ] \ No newline at end of file diff --git a/tests/Infrastructure/Common/TestComponents/ORM/MemoryQueueItemRepository.php b/tests/Infrastructure/Common/TestComponents/ORM/MemoryQueueItemRepository.php index 242f38b9..221f0374 100644 --- a/tests/Infrastructure/Common/TestComponents/ORM/MemoryQueueItemRepository.php +++ b/tests/Infrastructure/Common/TestComponents/ORM/MemoryQueueItemRepository.php @@ -22,17 +22,21 @@ class MemoryQueueItemRepository extends MemoryRepository implements QueueItemRep public $disabled = false; /** - * Finds list of earliest queued queue items per queue. Following list of criteria for searching must be satisfied: + * Finds list of earliest queued queue items per queue for given priority. + * Following list of criteria for searching must be satisfied: * - Queue must be without already running queue items * - For one queue only one (oldest queued) item should be returned + * - Only queue items with given priority can be retrieved. * + * @param int $priority Queue item priority priority. * @param int $limit Result set limit. By default max 10 earliest queue items will be returned * * @return QueueItem[] Found queue item list + * * @throws \Logeecom\Infrastructure\ORM\Exceptions\EntityClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException */ - public function findOldestQueuedItems($limit = 10) + public function findOldestQueuedItems($priority, $limit = 10) { $filter = new QueryFilter(); $filter->where('status', '=', QueueItem::IN_PROGRESS); @@ -45,6 +49,8 @@ public function findOldestQueuedItems($limit = 10) } $filter = new QueryFilter(); + + $filter->where('priority', Operators::EQUALS, $priority); $filter->where('status', '=', QueueItem::QUEUED); $filter->where('queueName', 'NOT IN', array_unique($runningQueuesQuery)); $filter->orderBy('queueTime', 'ASC'); diff --git a/tests/Infrastructure/Common/TestComponents/TaskExecution/BarTask.php b/tests/Infrastructure/Common/TestComponents/TaskExecution/BarTask.php index 7be8f32a..e6f05823 100644 --- a/tests/Infrastructure/Common/TestComponents/TaskExecution/BarTask.php +++ b/tests/Infrastructure/Common/TestComponents/TaskExecution/BarTask.php @@ -4,5 +4,10 @@ class BarTask extends FooTask { + public function execute() + { + parent::execute(); + $this->reportProgress(100); + } } \ No newline at end of file diff --git a/tests/Infrastructure/Common/TestComponents/TaskExecution/FakeRunnable.php b/tests/Infrastructure/Common/TestComponents/TaskExecution/FakeRunnable.php new file mode 100644 index 00000000..7d7e1e21 --- /dev/null +++ b/tests/Infrastructure/Common/TestComponents/TaskExecution/FakeRunnable.php @@ -0,0 +1,85 @@ +testProperty = $testProperty; + } + + public function getMethodCallHistory($methodName) + { + return !empty($this->callHistory[$methodName]) ? $this->callHistory[$methodName] : array(); + } + + /** + * @inheritDoc + */ + public function serialize() + { + return Serializer::serialize(array($this->testProperty, $this->callHistory)); + } + + /** + * @inheritDoc + */ + public function unserialize($serialized) + { + list($this->testProperty, $this->callHistory) = Serializer::unserialize($serialized); + } + + /** + * @inheritDoc + */ + public static function fromArray(array $data) + { + $instance = new self($data['testProperty']); + + $instance->callHistory = $data['callHistory']; + + return $instance; + } + + /** + * @inheritDoc + */ + public function toArray() + { + return array( + 'testProperty' => $this->testProperty, + 'callHistory' => $this->callHistory, + ); + } + + /** + * Starts runnable run logic. + */ + public function run() + { + $this->callHistory['run'][] = array(); + } + + /** + * @return mixed + */ + public function getTestProperty() + { + return $this->testProperty; + } + + /** + * @param mixed $testProperty + */ + public function setTestProperty($testProperty) + { + $this->testProperty = $testProperty; + } +} \ No newline at end of file diff --git a/tests/Infrastructure/Common/TestComponents/TaskExecution/TestAsyncProcessStarter.php b/tests/Infrastructure/Common/TestComponents/TaskExecution/TestAsyncProcessStarter.php new file mode 100644 index 00000000..4209da11 --- /dev/null +++ b/tests/Infrastructure/Common/TestComponents/TaskExecution/TestAsyncProcessStarter.php @@ -0,0 +1,48 @@ +doStartRunner = $doStartRunner; + } + + public function getMethodCallHistory($methodName) + { + return !empty($this->callHistory[$methodName]) ? $this->callHistory[$methodName] : array(); + } + + public function start(Runnable $runner) + { + $this->callHistory['start'][] = array('runner' => $runner); + if ($this->doStartRunner) { + $runner->run(); + } + } + + /** + * @param bool $doStartRunner + */ + public function setDoStartRunner($doStartRunner) + { + $this->doStartRunner = $doStartRunner; + } + + /** + * @inheritDoc + */ + public function runProcess($guid) + { + } +} \ No newline at end of file diff --git a/tests/Infrastructure/Common/TestComponents/TestAsyncSocketHttpClient.php b/tests/Infrastructure/Common/TestComponents/TestAsyncSocketHttpClient.php new file mode 100644 index 00000000..f00de3fd --- /dev/null +++ b/tests/Infrastructure/Common/TestComponents/TestAsyncSocketHttpClient.php @@ -0,0 +1,21 @@ +requestHistory[] = array( + 'transferProtocol' => $transferProtocol, + 'host' => $host, + 'port' => $port, + 'timeout' => $timeOut, + 'payload' => $payload, + ); + } +} \ No newline at end of file diff --git a/tests/Infrastructure/Common/TestComponents/TestConfigurationManager.php b/tests/Infrastructure/Common/TestComponents/TestConfigurationManager.php new file mode 100644 index 00000000..05536a73 --- /dev/null +++ b/tests/Infrastructure/Common/TestComponents/TestConfigurationManager.php @@ -0,0 +1,24 @@ +responses); - $headers = !empty($response['headers']) ? $response['headers'] : array(); + $headers = !empty($response['headers']) ? $response['headers'] : array(); return array($response['data'], $response['status'], $headers); } diff --git a/tests/Infrastructure/Common/TestComponents/TestHttpClient.php b/tests/Infrastructure/Common/TestComponents/TestHttpClient.php index ad4bfb37..40186235 100644 --- a/tests/Infrastructure/Common/TestComponents/TestHttpClient.php +++ b/tests/Infrastructure/Common/TestComponents/TestHttpClient.php @@ -5,7 +5,7 @@ namespace Logeecom\Tests\Infrastructure\Common\TestComponents; -use Logeecom\Infrastructure\Http\DTO\OptionsDTO; +use Logeecom\Infrastructure\Http\DTO\Options; use Logeecom\Infrastructure\Http\Exceptions\HttpCommunicationException; use Logeecom\Infrastructure\Http\HttpClient; @@ -89,8 +89,8 @@ protected function getAutoConfigurationOptionsCombinations($method, $url) if (empty($this->autoConfigurationCombinations)) { $this->setAdditionalOptionsCombinations( array( - array(new OptionsDTO(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4)), - array(new OptionsDTO(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6)), + array(new Options(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4)), + array(new Options(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6)), ) ); } @@ -112,7 +112,7 @@ protected function setAdditionalOptionsCombinations(array $combinations) * Save additional options for request. * * @param string $domain A domain for which to reset configuration options. - * @param OptionsDTO[] $options Additional option to add to HTTP request. + * @param Options[] $options Additional option to add to HTTP request. */ protected function setAdditionalOptions($domain, $options) { diff --git a/tests/Infrastructure/Http/AsyncSocketHttpClientTest.php b/tests/Infrastructure/Http/AsyncSocketHttpClientTest.php new file mode 100644 index 00000000..3c4007fd --- /dev/null +++ b/tests/Infrastructure/Http/AsyncSocketHttpClientTest.php @@ -0,0 +1,119 @@ +client = new TestAsyncSocketHttpClient(); + } + + public function testTransferProtocolHttps() + { + // arrange + $url = 'https://google.com'; + + // act + $this->client->requestAsync('GET', $url); + + // assert + $this->assertEquals('tls://', $this->client->requestHistory[0]['transferProtocol']); + } + + public function testTransferProtocolHttp() + { + // arrange + $url = 'http://google.com'; + + // act + $this->client->requestAsync('GET', $url); + + // assert + $this->assertEquals('tcp://', $this->client->requestHistory[0]['transferProtocol']); + } + + public function testHost() + { + // arrange + $url = 'http://user:password@google.com/some/path?query=test#fragment'; + + // act + $this->client->requestAsync('GET', $url); + + // assert + $this->assertEquals('google.com', $this->client->requestHistory[0]['host']); + } + + public function testDefaultHttpsPort() + { + // arrange + $url = 'https://user:password@google.com/some/path?query=test#fragment'; + + // act + $this->client->requestAsync('GET', $url); + + // assert + $this->assertEquals(443, $this->client->requestHistory[0]['port']); + } + + public function testDefaultHttpPort() + { + // arrange + $url = 'http://user:password@google.com/some/path?query=test#fragment'; + + // act + $this->client->requestAsync('GET', $url); + + // assert + $this->assertEquals(80, $this->client->requestHistory[0]['port']); + } + + public function testCustomHttpPort() + { + // arrange + $url = 'http://user:password@google.com:1234/some/path?query=test#fragment'; + + // act + $this->client->requestAsync('GET', $url); + + // assert + $this->assertEquals(1234, $this->client->requestHistory[0]['port']); + } + + public function testDefaultTimeout() + { + // arrange + $url = 'http://user:password@google.com/some/path?query=test#fragment'; + + // act + $this->client->requestAsync('GET', $url); + + // assert + $this->assertEquals(5, $this->client->requestHistory[0]['timeout']); + } + + public function testCustomTimeout() + { + // arrange + $url = 'http://user:password@google.com/some/path?query=test#fragment'; + /** @var Configuration $configService */ + $configService = TestServiceRegister::getService(Configuration::CLASS_NAME); + $configService->setAsyncRequestTimeout(10); + + // act + $this->client->requestAsync('GET', $url); + + // assert + $this->assertEquals(10, $this->client->requestHistory[0]['timeout']); + } +} \ No newline at end of file diff --git a/tests/Infrastructure/Http/AutoConfigurationCurlTest.php b/tests/Infrastructure/Http/AutoConfigurationCurlTest.php index 54bcca2c..bcaf47a3 100644 --- a/tests/Infrastructure/Http/AutoConfigurationCurlTest.php +++ b/tests/Infrastructure/Http/AutoConfigurationCurlTest.php @@ -6,7 +6,7 @@ use Logeecom\Infrastructure\Http\AutoConfiguration; use Logeecom\Infrastructure\Http\CurlHttpClient; -use Logeecom\Infrastructure\Http\DTO\OptionsDTO; +use Logeecom\Infrastructure\Http\DTO\Options; use Logeecom\Infrastructure\Http\HttpClient; use Logeecom\Tests\Infrastructure\Common\BaseInfrastructureTestWithServices; use Logeecom\Tests\Infrastructure\Common\TestComponents\TestCurlHttpClient; @@ -81,7 +81,7 @@ public function testAutoConfigureSuccessWithSomeCombination() $this->getResponse(200), ); $this->httpClient->setMockResponses($responses); - $additionalOptionsCombination = array(new OptionsDTO(CurlHttpClient::SWITCH_PROTOCOL, true)); + $additionalOptionsCombination = array(new Options(CurlHttpClient::SWITCH_PROTOCOL, true)); $controller = new AutoConfiguration($this->shopConfig, $this->httpClient); $success = $controller->start(); @@ -118,9 +118,9 @@ public function testAutoConfigureSuccessWithAllCombination() ); $this->httpClient->setMockResponses($responses); $additionalOptionsCombination = array( - new OptionsDTO(CurlHttpClient::SWITCH_PROTOCOL, true), - new OptionsDTO(CURLOPT_FOLLOWLOCATION, false), - new OptionsDTO(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), + new Options(CurlHttpClient::SWITCH_PROTOCOL, true), + new Options(CURLOPT_FOLLOWLOCATION, false), + new Options(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), ); $controller = new AutoConfiguration($this->shopConfig, $this->httpClient); diff --git a/tests/Infrastructure/Http/AutoConfigurationTest.php b/tests/Infrastructure/Http/AutoConfigurationTest.php index c1de599c..30fc20e5 100644 --- a/tests/Infrastructure/Http/AutoConfigurationTest.php +++ b/tests/Infrastructure/Http/AutoConfigurationTest.php @@ -3,7 +3,7 @@ namespace Logeecom\Tests\Infrastructure\Http; use Logeecom\Infrastructure\Http\AutoConfiguration; -use Logeecom\Infrastructure\Http\DTO\OptionsDTO; +use Logeecom\Infrastructure\Http\DTO\Options; use Logeecom\Infrastructure\Http\HttpClient; use Logeecom\Infrastructure\Http\HttpResponse; use Logeecom\Tests\Infrastructure\Common\BaseInfrastructureTestWithServices; @@ -26,12 +26,11 @@ public function setUp() $this->httpClient = new TestHttpClient(); $me = $this; - new TestServiceRegister( - array( - HttpClient::CLASS_NAME => function () use ($me) { - return $me->httpClient; - }, - ) + TestServiceRegister::registerService( + HttpClient::CLASS_NAME, + function () use ($me) { + return $me->httpClient; + } ); $this->shopConfig->setAutoConfigurationUrl('http://example.com'); @@ -80,7 +79,7 @@ public function testAutoConfigureSuccessfullyWithSomeCombination() new HttpResponse(200, array(), '{}'), ); $this->httpClient->setMockResponses($responses); - $additionalOptionsCombination = array(new OptionsDTO(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4)); + $additionalOptionsCombination = array(new Options(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4)); $controller = new AutoConfiguration($this->shopConfig, $this->httpClient); $success = $controller->start(); diff --git a/tests/Infrastructure/Http/CurlHttpClientTest.php b/tests/Infrastructure/Http/CurlHttpClientTest.php deleted file mode 100644 index 074cd352..00000000 --- a/tests/Infrastructure/Http/CurlHttpClientTest.php +++ /dev/null @@ -1,269 +0,0 @@ -httpClient = new TestCurlHttpClient(); - $me = $this; - TestServiceRegister::registerService( - HttpClient::CLASS_NAME, - function () use ($me) { - return $me->httpClient; - } - ); - } - - /** - * Test a sync call. - */ - public function testSyncCall() - { - $responses = array($this->getResponse(200)); - - $this->successCall($responses); - $this->assertCallTimeout(CurlHttpClient::DEFAULT_REQUEST_TIMEOUT); - } - - /** - * Test a sync call. - */ - public function testSyncCallWithDifferentTimeout() - { - $newTimeout = 20000; - $this->shopConfig->setSyncRequestTimeout($newTimeout); - - $responses = array($this->getResponse(200)); - - $this->successCall($responses); - $this->assertCallTimeout($newTimeout); - } - - /** - * Test an async call. - */ - public function testDefaultAsyncCall() - { - $responses = array($this->getResponse(200)); - - $this->httpClient->setMockResponses($responses); - - $this->httpClient->requestAsync('POST', 'test.url.com'); - - $history = $this->httpClient->getHistory(); - $this->assertCount(1, $history); - $this->assertEquals( - TestCurlHttpClient::REQUEST_TYPE_ASYNCHRONOUS, - $history[0]['type'], - 'Async call should pass.' - ); - $this->assertCallTimeout(CurlHttpClient::DEFAULT_ASYNC_REQUEST_TIMEOUT); - } - - /** - * Test an async call with custom timeout. - */ - public function testDefaultAsyncCallDifferentTimeout() - { - $responses = array($this->getResponse(200)); - - $this->httpClient->setMockResponses($responses); - - $newTimeout = 200; - $this->shopConfig->setAsyncRequestTimeout($newTimeout); - $this->httpClient->requestAsync('POST', 'test.url.com'); - - $this->assertCallTimeout($newTimeout); - } - - /** - * Test async call with progress callback - */ - public function testAsyncCallWithProgressCallback() - { - $responses = array($this->getResponse(200)); - $this->httpClient->setMockResponses($responses); - - $this->shopConfig->setAsyncRequestWithProgress(true); - $this->httpClient->requestAsync('POST', 'test.url.com'); - - $this->assertProgressCallback(); - $this->assertCallTimeout(CurlHttpClient::DEFAULT_ASYNC_REQUEST_WITH_PROGRESS_TIMEOUT); - } - - /** - * Test async call without progress callback - */ - public function testAsyncCallWithoutProgressCallback() - { - $responses = array($this->getResponse(200)); - - $this->httpClient->setMockResponses($responses); - - $this->shopConfig->setAsyncRequestWithProgress(false); - $this->httpClient->requestAsync('POST', 'test.url.com'); - - $this->assertProgressCallback(false); - $this->assertCallTimeout(CurlHttpClient::DEFAULT_ASYNC_REQUEST_TIMEOUT); - } - - /** - * Test an async call with custom timeout and progress callback. - */ - public function testDAsyncCallWithProgressCallbackDifferentTimeout() - { - $responses = array($this->getResponse(200)); - - $this->httpClient->setMockResponses($responses); - - $newTimeout = 200; - $this->shopConfig->setAsyncRequestWithProgress(true); - $this->shopConfig->setAsyncRequestTimeout($newTimeout); - $this->httpClient->requestAsync('POST', 'test.url.com'); - - $this->assertCallTimeout($newTimeout); - } - - /** - * Test parsing plain text response. - */ - public function testParsingResponse() - { - $response = $this->successCall(array($this->getResponse(200))); - - $this->assertEquals(200, $response->getStatus()); - $headers = $response->getHeaders(); - $this->assertCount(9, $headers); - $this->assertEquals('HTTP/1.1 200 CUSTOM', $headers[0]); - - $this->assertTrue(array_key_exists('Cache-Control', $headers)); - $this->assertEquals('no-cache', $headers['Cache-Control']); - $this->assertTrue(array_key_exists('Server', $headers)); - $this->assertEquals('test', $headers['Server']); - $this->assertTrue(array_key_exists('Date', $headers)); - $this->assertEquals('Wed Jul 4 15:32:03 2019', $headers['Date']); - $this->assertTrue(array_key_exists('Connection', $headers)); - $this->assertEquals('Keep-Alive:', $headers['Connection']); - $this->assertTrue(array_key_exists('Content-Type', $headers)); - $this->assertEquals('application/json', $headers['Content-Type']); - $this->assertTrue(array_key_exists('Content-Length', $headers)); - $this->assertEquals('24860', $headers['Content-Length']); - $this->assertTrue(array_key_exists('X-Custom-Header', $headers)); - $this->assertEquals('Content: database', $headers['X-Custom-Header']); - - $body = json_decode($response->getBody(), true); - $this->assertCount(1, $body); - $this->assertTrue(array_key_exists('status', $body)); - $this->assertEquals('success', $body['status']); - } - - /** - * Tests setting follow location. - */ - public function testFollowLocation() - { - $this->httpClient->setFollowLocation(false); - $this->successCall(array($this->getResponse(200))); - - $options = $this->httpClient->getCurlOptions(); - $this->assertArrayNotHasKey(CURLOPT_FOLLOWLOCATION, $options, 'Curl FOLLOWLOCATION should be set.'); - } - - private function successCall($responses) - { - $this->httpClient->setMockResponses($responses); - $success = $this->httpClient->request('POST', 'test.url.com'); - - $this->assertTrue($success->isSuccessful(), 'Sync call should pass.'); - $this->assertCount(1, $this->httpClient->getHistory()); - $this->assertNotEmpty($this->httpClient->getCurlOptions(), 'Curl options should be set.'); - - return $success; - } - - private function getResponse($code) - { - return array( - 'status' => $code, - 'headers' => array( - "HTTP/1.1 $code CUSTOM", - 'Cache-Control' => 'no-cache', - 'Server' => 'test', - 'Location' => 'https://test.url.com', - 'Date' => 'Wed Jul 4 15:32:03 2019', - 'Connection' => 'Keep-Alive:', - 'Content-Type' => 'application/json', - 'Content-Length' => '24860', - 'X-Custom-Header' => 'Content: database', - ), - 'data' => "{\"status\":\"success\"}", - ); - } - - /** - * @param $timeout - */ - private function assertCallTimeout($timeout) - { - $curlOptions = $this->httpClient->getCurlOptions(); - $this->assertNotEmpty($curlOptions, 'Curl options should be set.'); - $this->assertTrue(isset($curlOptions[CURLOPT_TIMEOUT_MS]), 'Curl timeout should be set for async call.'); - $this->assertEquals( - $timeout, - $curlOptions[CURLOPT_TIMEOUT_MS], - 'Curl default timeout should be set for async call.' - ); - } - - private function assertProgressCallback($isOn = true) - { - $curlOptions = $this->httpClient->getCurlOptions(); - $this->assertNotEmpty($curlOptions, 'Curl options should be set.'); - if (!$isOn) { - $this->assertFalse( - isset($curlOptions[CURLOPT_NOPROGRESS]), - 'Curl progress callback should not be set.' - ); - $this->assertFalse( - isset($curlOptions[CURLOPT_PROGRESSFUNCTION]), - 'Curl progress callback should not be set.' - ); - - return; - } - - - $this->assertTrue( - isset($curlOptions[CURLOPT_NOPROGRESS]), - 'Curl progress callback should be set for async call.' - ); - $this->assertFalse( - (bool)$curlOptions[CURLOPT_NOPROGRESS], - 'Curl progress callback should be set for async call.' - ); - $this->assertTrue( - isset($curlOptions[CURLOPT_PROGRESSFUNCTION]), - 'Curl progress callback should be set for async call.' - ); - $this->assertEquals( - array($this->httpClient, 'abortAfterAsyncRequestCallback'), - $curlOptions[CURLOPT_PROGRESSFUNCTION], - 'Curl progress callback should be set for async call.' - ); - } -} diff --git a/tests/Infrastructure/Http/HttpClientTest.php b/tests/Infrastructure/Http/HttpClientTest.php index 6d58fb29..2c46d38c 100644 --- a/tests/Infrastructure/Http/HttpClientTest.php +++ b/tests/Infrastructure/Http/HttpClientTest.php @@ -2,7 +2,7 @@ namespace Logeecom\Tests\Infrastructure\Http; -use Logeecom\Infrastructure\Http\DTO\OptionsDTO; +use Logeecom\Infrastructure\Http\DTO\Options; use Logeecom\Infrastructure\Http\HttpClient; use Logeecom\Infrastructure\Http\HttpResponse; use Logeecom\Tests\Infrastructure\Common\TestComponents\TestHttpClient; @@ -58,7 +58,7 @@ public function testAutoConfigureSuccessfullyWithSomeCombination() new HttpResponse(200, array(), '{}'), ); $this->httpClient->setMockResponses($responses); - $additionalOptionsCombination = array(new OptionsDTO(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4)); + $additionalOptionsCombination = array(new Options(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4)); $success = $this->httpClient->autoConfigure('POST', 'test.url.com'); diff --git a/tests/Infrastructure/ORM/AbstractGenericQueueItemRepositoryTest.php b/tests/Infrastructure/ORM/AbstractGenericQueueItemRepositoryTest.php index 1a9b2247..23627eb8 100644 --- a/tests/Infrastructure/ORM/AbstractGenericQueueItemRepositoryTest.php +++ b/tests/Infrastructure/ORM/AbstractGenericQueueItemRepositoryTest.php @@ -2,9 +2,11 @@ namespace Logeecom\Tests\Infrastructure\ORM; +use DateTime; use Logeecom\Infrastructure\ORM\QueryFilter\QueryFilter; use Logeecom\Infrastructure\ORM\RepositoryRegistry; use Logeecom\Infrastructure\Serializer\Serializer; +use Logeecom\Infrastructure\TaskExecution\Interfaces\Priority; use Logeecom\Infrastructure\TaskExecution\QueueItem; use Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\BarTask; use Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\FooTask; @@ -44,28 +46,25 @@ public function testRegisteredRepositories() } /** - * @depends testRegisteredRepositories * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException */ public function testQueueItemMassInsert() { - $repository = RepositoryRegistry::getQueueItemRepository(); - - foreach ($this->readQueueItemsFromFile() as $entity) { - $id = $repository->save($entity); + $insertedIds = $this->insertQueueItems(); + foreach ($insertedIds as $id) { $this->assertGreaterThan(0, $id); } } /** - * @depends testQueueItemMassInsert * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException */ public function testUpdate() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); $queryFilter = new QueryFilter(); $queryFilter->where('taskType', '=', 'FooTask'); @@ -86,25 +85,25 @@ public function testUpdate() } /** - * @depends testQueueItemMassInsert * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException */ public function testQueryAllQueueItems() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); $this->assertCount($this->queueItemCount, $repository->select()); } /** - * @depends testQueueItemMassInsert * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException */ public function testQueryWithFiltersString() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); $queryFilter = new QueryFilter(); $queryFilter->where('taskType', '=', 'FooTask'); @@ -117,13 +116,13 @@ public function testQueryWithFiltersString() } /** - * @depends testQueueItemMassInsert * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException */ public function testQueryWithFiltersInt() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); $queryFilter = new QueryFilter(); $queryFilter->where('lastExecutionProgress', '>', 0); @@ -140,16 +139,16 @@ public function testQueryWithFiltersInt() } /** - * @depends testQueueItemMassInsert * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException */ public function testQueryWithFiltersAndSort() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); $queryFilter = new QueryFilter(); - $queryFilter->where('queueTime', '<', \DateTime::createFromFormat('Y-m-d', '2017-07-01')); + $queryFilter->where('queueTime', '<', DateTime::createFromFormat('Y-m-d', '2017-07-01')); $queryFilter->orderBy('queueTime', 'DESC'); $results = $repository->select($queryFilter); @@ -157,16 +156,16 @@ public function testQueryWithFiltersAndSort() } /** - * @depends testQueueItemMassInsert * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException */ public function testQueryWithFiltersAndLimit() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); $queryFilter = new QueryFilter(); - $queryFilter->where('queueTime', '<', \DateTime::createFromFormat('Y-m-d', '2017-07-01')); + $queryFilter->where('queueTime', '<', DateTime::createFromFormat('Y-m-d', '2017-07-01')); $queryFilter->setLimit(5); $results = $repository->select($queryFilter); @@ -174,21 +173,19 @@ public function testQueryWithFiltersAndLimit() } /** - * @depends testQueueItemMassInsert * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException */ public function testFindOldestQueuedItems() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); - $this->assertCount(2, $repository->findOldestQueuedItems()); - $this->assertTrue(true); + $this->assertCount(1, $repository->findOldestQueuedItems(Priority::LOW)); + $this->assertCount(1, $repository->findOldestQueuedItems(Priority::NORMAL)); } /** - * @depends testQueueItemMassInsert - * * @expectedException \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemSaveException * * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException @@ -198,6 +195,7 @@ public function testFindOldestQueuedItems() */ public function testSaveWithCondition() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); $queryFilter = new QueryFilter(); $queryFilter->where('lastUpdateTimestamp', '=', 1493851325); @@ -223,8 +221,6 @@ public function testSaveWithCondition() } /** - * @depends testQueueItemMassInsert - * * @expectedException \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemSaveException * * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException @@ -234,6 +230,7 @@ public function testSaveWithCondition() */ public function testSaveWithConditionWithNull() { + $this->insertQueueItems(); $repository = RepositoryRegistry::getQueueItemRepository(); $queryFilter = new QueryFilter(); $queryFilter->where('lastUpdateTimestamp', '=', 1518325751); @@ -284,6 +281,22 @@ protected function tearDown() parent::tearDown(); } + /** + * @return array + * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryClassException + * @throws \Logeecom\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException + */ + protected function insertQueueItems() + { + $repository = RepositoryRegistry::getQueueItemRepository(); + $ids = array(); + foreach ($this->readQueueItemsFromFile() as $entity) { + $ids[] = $repository->save($entity); + } + + return $ids; + } + /** * Reads test data fixtures about queue items from file * @@ -315,6 +328,7 @@ protected function readQueueItemsFromFile() $queueItem->setLastUpdateTimestamp($item['lastUpdateTimestamp']); $queueItem->setFinishTimestamp($item['finishTimestamp']); $queueItem->setFailTimestamp($item['failTimestamp']); + $queueItem->setPriority($item['priority']); $queueItems[] = $queueItem; } diff --git a/tests/Infrastructure/ORM/EntityTranslatorTest.php b/tests/Infrastructure/ORM/EntityTranslatorTest.php index 1b3a8c27..ccab00ae 100644 --- a/tests/Infrastructure/ORM/EntityTranslatorTest.php +++ b/tests/Infrastructure/ORM/EntityTranslatorTest.php @@ -5,6 +5,7 @@ use Logeecom\Infrastructure\ORM\IntermediateObject; use Logeecom\Infrastructure\ORM\Utility\EntityTranslator; use Logeecom\Infrastructure\Serializer\Serializer; +use Logeecom\Infrastructure\TaskExecution\Interfaces\Priority; use Logeecom\Infrastructure\TaskExecution\QueueItem; use Logeecom\Infrastructure\TaskExecution\TaskRunnerStatus; use Logeecom\Tests\Infrastructure\Common\BaseInfrastructureTestWithServices; @@ -29,6 +30,7 @@ public function testTranslate() $entity->setLastUpdateTimestamp(time()); $entity->setFailTimestamp(time()); $entity->setFinishTimestamp(time()); + $entity->setPriority(Priority::LOW); $intermediate = new IntermediateObject(); $data = $entity->toArray(); diff --git a/tests/Infrastructure/ORM/MemoryGenericQueueItemRepositoryTest.php b/tests/Infrastructure/ORM/MemoryGenericQueueItemRepositoryTest.php index dd115afc..32bf11c1 100644 --- a/tests/Infrastructure/ORM/MemoryGenericQueueItemRepositoryTest.php +++ b/tests/Infrastructure/ORM/MemoryGenericQueueItemRepositoryTest.php @@ -3,6 +3,7 @@ namespace Logeecom\Tests\Infrastructure\ORM; use Logeecom\Tests\Infrastructure\Common\TestComponents\ORM\MemoryQueueItemRepository; +use Logeecom\Tests\Infrastructure\Common\TestComponents\ORM\MemoryStorage; /** * Class MemoryGenericQueueItemRepositoryTest. @@ -24,5 +25,6 @@ public function getQueueItemEntityRepositoryClass() */ public function cleanUpStorage() { + MemoryStorage::reset(); } } diff --git a/tests/Infrastructure/TaskExecution/AsyncBatchStarterTest.php b/tests/Infrastructure/TaskExecution/AsyncBatchStarterTest.php new file mode 100644 index 00000000..faa21708 --- /dev/null +++ b/tests/Infrastructure/TaskExecution/AsyncBatchStarterTest.php @@ -0,0 +1,186 @@ +asyncProcessStarter = $asyncProcessStarter; + } + + public function testCreationWhenItemCountIsLessThanBatchSize() + { + $batchStarter = new AsyncBatchStarter(2, array(new FakeRunnable())); + + $this->assertSame(0, $batchStarter->getMaxNestingLevels(), 'AsyncBatchStarter should create sub-levels only when batch size is exceeded.'); + } + + public function testCreationWhenItemCountIsGreaterThanBatchSize() + { + $batchStarter = new AsyncBatchStarter(2, array(new FakeRunnable(), new FakeRunnable(), new FakeRunnable())); + + $this->assertSame(1, $batchStarter->getMaxNestingLevels(), 'AsyncBatchStarter should create sub-levels when batch size is exceeded.'); + } + + public function testCreationWhenItemCountIsMuchGreaterThanBatchSize() + { + $batchStarter = new AsyncBatchStarter( + 2, + array( + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable() + ) + ); + + $this->assertSame(2, $batchStarter->getMaxNestingLevels(), 'AsyncBatchStarter should create sub-sub-levels when batch size is exceeded.'); + } + + /** + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\ProcessStarterSaveException + */ + public function testRunningBatchWithoutNestingLevel() + { + $testRunner = new FakeRunnable(); + $batchStarter = new AsyncBatchStarter(2, array($testRunner)); + + $batchStarter->run(); + + $startCallHistory = $this->asyncProcessStarter->getMethodCallHistory('start'); + $this->assertCount(1, $startCallHistory, 'AsyncBatchStarter should start runners immediately when there are no sub-batches.'); + $this->assertEquals($testRunner, $startCallHistory[0]['runner'], 'AsyncBatchStarter should start runners immediately when there are no sub-batches.'); + } + + /** + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\ProcessStarterSaveException + */ + public function testRunningBatchWithNestingLevel() + { + $testRunner = new FakeRunnable(); + $batchStarter = new AsyncBatchStarter(2, array(new FakeRunnable(), new FakeRunnable(), $testRunner)); + + $batchStarter->run(); + + $startCallHistory = $this->asyncProcessStarter->getMethodCallHistory('start'); + $this->assertCount(4, $startCallHistory, 'AsyncBatchStarter should start sub-batches.'); + $this->assertEquals($testRunner, $startCallHistory[3]['runner'], 'AsyncBatchStarter should start sub-batches.'); + } + + /** + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\ProcessStarterSaveException + */ + public function testRunningBatchAfterSerialization() + { + $testRunnerProperty = 'serialization running test'; + $testRunner = new FakeRunnable($testRunnerProperty); + $batchStarter = new AsyncBatchStarter(2, array(new FakeRunnable(), new FakeRunnable(), $testRunner)); + /** @var AsyncBatchStarter $batchStarter */ + $batchStarter = Serializer::unserialize(Serializer::serialize($batchStarter)); + + $batchStarter->run(); + + $startCallHistory = $this->asyncProcessStarter->getMethodCallHistory('start'); + $this->assertCount(4, $startCallHistory, 'AsyncBatchStarter should be serializable.'); + /** @var FakeRunnable $runner */ + $runner = $startCallHistory[3]['runner']; + $this->assertEquals($testRunnerProperty, $runner->getTestProperty(), 'AsyncBatchStarter should be serializable.'); + } + + public function testBatchToString() + { + $batchStarter1 = new AsyncBatchStarter(2, array(new FakeRunnable(), new FakeRunnable(), new FakeRunnable())); + $batchStarter2 = new AsyncBatchStarter( + 2, + array( + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable() + ) + ); + $batchStarter3 = new AsyncBatchStarter( + 3, + array( + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable() + ) + ); + + $this->assertSame('B(B(R, R), R)', (string)$batchStarter1); + $this->assertSame(1, $batchStarter1->getMaxNestingLevels()); + $this->assertSame('B(B(B(R, R), B(R, R), R), B(B(R, R), R, R), R, R)', (string)$batchStarter2); + $this->assertSame(2, $batchStarter2->getMaxNestingLevels()); + $this->assertSame('B(B(R, R, R), B(R, R, R), B(R, R, R), R, R)', (string)$batchStarter3); + $this->assertSame(1, $batchStarter3->getMaxNestingLevels()); + } + + public function testWaitTimeCalculation() + { + $requestTimeout = 1; + $batchStarter1 = new AsyncBatchStarter(2, array(new FakeRunnable(), new FakeRunnable(), new FakeRunnable())); + $batchStarter2 = new AsyncBatchStarter( + 2, + array( + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), + new FakeRunnable() + ) + ); + $batchStarter3 = new AsyncBatchStarter( + 3, + array( + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable(), new FakeRunnable(), new FakeRunnable(), + new FakeRunnable() + ) + ); + + $this->assertSame('B(B(R, R), R)', (string)$batchStarter1); + $this->assertSame(1, $batchStarter1->getMaxNestingLevels()); + $this->assertSame(1, $batchStarter1->getWaitTime($requestTimeout), 'Wait time should be calculated as batchSize * maxNestingLevel * requestTimeout - runners out of batch * requestTimeout'); + $this->assertSame('B(B(B(R, R), B(R, R), R), B(B(R, R), R, R), R, R)', (string)$batchStarter2); + $this->assertSame(2, $batchStarter2->getMaxNestingLevels()); + $this->assertSame(2, $batchStarter2->getWaitTime($requestTimeout), 'Wait time should be calculated as batchSize * maxNestingLevel * requestTimeout - runners out of batch * requestTimeout'); + $this->assertSame('B(B(R, R, R), B(R, R, R), B(R, R, R), R)', (string)$batchStarter3); + $this->assertSame(1, $batchStarter3->getMaxNestingLevels()); + $this->assertSame(2, $batchStarter3->getWaitTime($requestTimeout), 'Wait time should be calculated as batchSize * maxNestingLevel * requestTimeout - runners out of batch * requestTimeout'); + } +} \ No newline at end of file diff --git a/tests/Infrastructure/TaskExecution/QueueItemEntityTest.php b/tests/Infrastructure/TaskExecution/QueueItemEntityTest.php index 33c11721..38e0677f 100644 --- a/tests/Infrastructure/TaskExecution/QueueItemEntityTest.php +++ b/tests/Infrastructure/TaskExecution/QueueItemEntityTest.php @@ -4,6 +4,7 @@ use Logeecom\Infrastructure\Serializer\Concrete\NativeSerializer; use Logeecom\Infrastructure\Serializer\Serializer; +use Logeecom\Infrastructure\TaskExecution\Interfaces\Priority; use Logeecom\Infrastructure\TaskExecution\QueueItem; use Logeecom\Infrastructure\Utility\TimeProvider; use Logeecom\Tests\Infrastructure\Common\TestComponents\TaskExecution\FooTask; @@ -143,6 +144,7 @@ public function testFromArrayAndToJSON() 'earliestStartTime' => $earliestTime->format(DATE_ATOM), 'queueTime' => $queueTime->format(DATE_ATOM), 'lastUpdateTime' => $lastUpdateTime->format(DATE_ATOM), + 'priority' => Priority::LOW, ); $entity = QueueItem::fromArray($data); diff --git a/tests/Infrastructure/TaskExecution/QueueTest.php b/tests/Infrastructure/TaskExecution/QueueTest.php index f1bbf075..f2be6387 100644 --- a/tests/Infrastructure/TaskExecution/QueueTest.php +++ b/tests/Infrastructure/TaskExecution/QueueTest.php @@ -3,6 +3,7 @@ namespace Logeecom\Tests\Infrastructure\TaskExecution; +use DateTime; use Logeecom\Infrastructure\Configuration\ConfigEntity; use Logeecom\Infrastructure\Configuration\Configuration; use Logeecom\Infrastructure\ORM\QueryFilter\QueryFilter; @@ -176,6 +177,7 @@ public function testItShouldBePossibleToFindQueueItemById() /** * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testItShouldBePossibleToFindRunningQueueItems() { @@ -229,7 +231,7 @@ public function testItShouldBePossibleToFindRunningQueueItems() public function testFindOldestQueuedItems() { // Arrange - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -3 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -3 days')); $earliestQueue1Item = $this->queue->enqueue( 'queue1', new FooTask() @@ -244,7 +246,7 @@ public function testFindOldestQueuedItems() new FooTask() ); - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -2 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -2 days')); $this->queue->enqueue( 'queue1', new FooTask() @@ -284,7 +286,7 @@ public function testFindOldestQueuedItems() public function testFindLatestByType() { // Arrange - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -3 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -3 days')); $this->queue->enqueue( 'queue1', new FooTask(), @@ -292,14 +294,14 @@ public function testFindLatestByType() ); $this->queue->enqueue('queue2', new FooTask(), 'context'); - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -2 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -2 days')); $latestQueueItem = $this->queue->enqueue( 'queue1', new FooTask(), 'context' ); - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -1 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -1 days')); $this->queue->enqueue('queue1', new BarTask(), 'context'); $globallyLatestQueueItem = $this->queue->enqueue( 'queue1', @@ -339,24 +341,24 @@ public function testFindLatestByType() public function testLimitOfFinOldestQueuedItems() { // Arrange - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -2 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -2 days')); $this->queue->enqueue( 'queue5', new FooTask() ); - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -3 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -3 days')); $this->queue->enqueue( 'queue4', new FooTask() ); - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -4 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -4 days')); $earliestQueue3Item = $this->queue->enqueue( 'queue3', new FooTask() ); - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -5 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -5 days')); $earliestQueue2Item = $this->queue->enqueue('queue2', new FooTask()); - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -6 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -6 days')); $earliestQueue1Item = $this->queue->enqueue( 'queue1', new FooTask() @@ -389,7 +391,7 @@ public function testLimitOfFinOldestQueuedItems() public function testItShouldBePossibleEnqueueTaskToQueue() { // Arrange - $currentTime = new \DateTime(); + $currentTime = new DateTime(); $this->timeProvider->setCurrentLocalTime($currentTime); // Act @@ -495,11 +497,11 @@ public function testItShouldBePossibleToTransitToInProgressStateFromQueued() // Arrange $task = new FooTask(); - $queuedTime = new \DateTime('now -2 days'); + $queuedTime = new DateTime('now -2 days'); $this->timeProvider->setCurrentLocalTime($queuedTime); $queueItem = $this->queue->enqueue('testQueue', $task); - $startTime = new \DateTime('now -1 day'); + $startTime = new DateTime('now -1 day'); $this->timeProvider->setCurrentLocalTime($startTime); // Act @@ -551,6 +553,7 @@ public function testItShouldBePossibleToTransitToInProgressStateFromQueued() * @throws \Logeecom\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testWhenInProgressReportedProgressShouldBeStoredUsingQueue() { @@ -576,6 +579,7 @@ public function testWhenInProgressReportedProgressShouldBeStoredUsingQueue() * @expectedExceptionMessage Progress reported for not started queue item. * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testWhenNotInProgressReportedProgressShouldFailJob() { @@ -604,7 +608,7 @@ public function testWhenInProgressReportedAliveShouldBeStoredWithCurrentTimeAsLa $queueItem = $this->queue->enqueue('testQueue', $task); $this->queue->start($queueItem); - $lastSaveTime = new \DateTime(); + $lastSaveTime = new DateTime(); $this->timeProvider->setCurrentLocalTime($lastSaveTime); // Act @@ -629,15 +633,15 @@ public function testItShouldBePossibleToTransitToCompletedStateFromInProgress() // Arrange $task = new FooTask(); - $queuedTime = new \DateTime('now -3 days'); + $queuedTime = new DateTime('now -3 days'); $this->timeProvider->setCurrentLocalTime($queuedTime); $queueItem = $this->queue->enqueue('testQueue', $task); - $startTime = new \DateTime('now -2 days'); + $startTime = new DateTime('now -2 days'); $this->timeProvider->setCurrentLocalTime($startTime); $this->queue->start($queueItem); - $finishTime = new \DateTime('now -1 day'); + $finishTime = new DateTime('now -1 day'); $this->timeProvider->setCurrentLocalTime($finishTime); // Act @@ -693,11 +697,11 @@ public function testRequeueStartedTaskShouldReturnQueueItemInQueuedState() // Arrange $task = new FooTask(); - $queuedTime = new \DateTime('now -3 days'); + $queuedTime = new DateTime('now -3 days'); $this->timeProvider->setCurrentLocalTime($queuedTime); $queueItem = $this->queue->enqueue('testQueue', $task); - $startTime = new \DateTime('now -2 days'); + $startTime = new DateTime('now -2 days'); $this->timeProvider->setCurrentLocalTime($startTime); $this->queue->start($queueItem); @@ -739,16 +743,16 @@ public function testFailingLessThanMaxRetryTimesShouldReturnQueueItemInQueuedSta // Arrange $task = new FooTask(); - $queuedTime = new \DateTime('now -3 days'); + $queuedTime = new DateTime('now -3 days'); $this->timeProvider->setCurrentLocalTime($queuedTime); $queueItem = $this->queue->enqueue('testQueue', $task); - $startTime = new \DateTime('now -2 days'); + $startTime = new DateTime('now -2 days'); $this->timeProvider->setCurrentLocalTime($startTime); $queueItem->setLastExecutionProgressBasePoints(3123); $this->queue->start($queueItem); - $failTime = new \DateTime('now -1 day'); + $failTime = new DateTime('now -1 day'); $this->timeProvider->setCurrentLocalTime($failTime); // Act @@ -804,14 +808,14 @@ public function testFailingMoreThanMaxRetryTimesShouldTransitQueueItemInFailedSt // Arrange $task = new FooTask(); - $queuedTime = new \DateTime('now -3 days'); + $queuedTime = new DateTime('now -3 days'); $this->timeProvider->setCurrentLocalTime($queuedTime); $queueItem = $this->queue->enqueue('testQueue', $task); - $this->timeProvider->setCurrentLocalTime(new \DateTime('now -2 days')); + $this->timeProvider->setCurrentLocalTime(new DateTime('now -2 days')); $this->queue->start($queueItem); - $failTime = new \DateTime('now -1 day'); + $failTime = new DateTime('now -1 day'); $this->timeProvider->setCurrentLocalTime($failTime); // Act @@ -880,6 +884,10 @@ public function testFailMessages() /** * Test regular task abort. + * + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException */ public function testAbortQueueItemMethod() { @@ -899,6 +907,7 @@ public function testAbortQueueItemMethod() /** * Test regular task abort. + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException */ public function testAbortingQueueItemFromTask() { @@ -920,6 +929,9 @@ public function testAbortingQueueItemFromTask() /** * Test regular task abort. + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException */ public function testAbortingQueueItemAfterFailure() { @@ -944,6 +956,10 @@ public function testAbortingQueueItemAfterFailure() /** * @expectedException \BadMethodCallException + * + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException */ public function testStartingQueueItemAfterAbortion() { @@ -958,6 +974,8 @@ public function testStartingQueueItemAfterAbortion() /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "created" to "in_progress" + * + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException */ @@ -1035,6 +1053,8 @@ public function testItShouldBeForbiddenToTransitionFromQueuedToCompletedStatus() /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "in_progress" to "in_progress" + * + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException */ @@ -1054,8 +1074,10 @@ public function testItShouldBeForbiddenToTransitionFromInProgressToInProgressSta /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "failed" to "in_progress" + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testItShouldBeForbiddenToTransitionFromFailedToInProgressStatus() { @@ -1081,8 +1103,10 @@ public function testItShouldBeForbiddenToTransitionFromFailedToInProgressStatus( /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "failed" to "failed" + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testItShouldBeForbiddenToTransitionFromFailedFailedStatus() { @@ -1108,8 +1132,10 @@ public function testItShouldBeForbiddenToTransitionFromFailedFailedStatus() /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "failed" to "completed" + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testItShouldBeForbiddenToTransitionFromFailedCompletedStatus() { @@ -1135,8 +1161,10 @@ public function testItShouldBeForbiddenToTransitionFromFailedCompletedStatus() /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "completed" to "in_progress" + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testItShouldBeForbiddenToTransitionFromCompletedToInProgressStatus() { @@ -1157,8 +1185,10 @@ public function testItShouldBeForbiddenToTransitionFromCompletedToInProgressStat /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "completed" to "failed" + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testItShouldBeForbiddenToTransitionFromCompletedToFailedStatus() { @@ -1179,8 +1209,10 @@ public function testItShouldBeForbiddenToTransitionFromCompletedToFailedStatus() /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "completed" to "completed" + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testItShouldBeForbiddenToTransitionFromCompletedToCompletedStatus() { @@ -1201,6 +1233,8 @@ public function testItShouldBeForbiddenToTransitionFromCompletedToCompletedStatu /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "queued" to "aborted" + * + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException */ public function testItShouldBeForbiddenToTransitionFromQueuedToAbortedStatus() { @@ -1214,6 +1248,10 @@ public function testItShouldBeForbiddenToTransitionFromQueuedToAbortedStatus() /** * @expectedException \BadMethodCallException * @expectedExceptionMessage Illegal queue item state transition from "failed" to "aborted" + * + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException */ public function testItShouldBeForbiddenToTransitionFromFailedToAbortedStatus() { @@ -1253,8 +1291,10 @@ public function testWhenStoringQueueItemFailsEnqueueMethodMustFail() /** * @expectedException \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * * @expectedExceptionMessage Unable to update the task. Queue storage failed to save item. * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testWhenStoringQueueItemFailsStartMethodMustFail() { @@ -1275,8 +1315,10 @@ public function testWhenStoringQueueItemFailsStartMethodMustFail() /** * @expectedException \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * * @expectedExceptionMessage Unable to update the task. Queue storage failed to save item. * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testWhenStoringQueueItemFailsFailMethodMustFail() { @@ -1299,7 +1341,9 @@ public function testWhenStoringQueueItemFailsFailMethodMustFail() /** * @expectedException \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException * @expectedExceptionMessage Unable to update the task. Queue storage failed to save item. + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testWhenStoringQueueItemProgressFailsProgressMethodMustFail() { @@ -1322,7 +1366,9 @@ public function testWhenStoringQueueItemProgressFailsProgressMethodMustFail() /** * @expectedException \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException * @expectedExceptionMessage Unable to update the task. Queue storage failed to save item. + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testWhenStoringQueueItemAliveFailsAliveMethodMustFail() { @@ -1342,7 +1388,9 @@ public function testWhenStoringQueueItemAliveFailsAliveMethodMustFail() /** * @expectedException \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException * @expectedExceptionMessage Unable to update the task. Queue storage failed to save item. + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ public function testWhenStoringQueueItemFailsFinishMethodMustFail() { @@ -1422,8 +1470,10 @@ private function assertQueueItemIsSaved(QueueItem $queueItem) * @param \Logeecom\Infrastructure\TaskExecution\Task $task * * @return \Logeecom\Infrastructure\TaskExecution\QueueItem + * * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueItemDeserializationException * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\QueueStorageUnavailableException + * @throws \Logeecom\Infrastructure\TaskExecution\Exceptions\AbortTaskExecutionException */ private function generateRunningQueueItem($queueName, Task $task) {