Skip to content

Commit

Permalink
refactor(actions): move PdkActions to a proper service with interface (
Browse files Browse the repository at this point in the history
  • Loading branch information
EdieLemoine authored Oct 19, 2023
1 parent e7356af commit 94508ee
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 104 deletions.
7 changes: 7 additions & 0 deletions config/pdk-services.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use MyParcelNL\Pdk\Api\Contract\ApiServiceInterface;
use MyParcelNL\Pdk\Api\Service\MyParcelApiService;
use MyParcelNL\Pdk\App\Account\Contract\PdkAccountRepositoryInterface;
use MyParcelNL\Pdk\App\Api\Contract\PdkActionsServiceInterface;
use MyParcelNL\Pdk\App\Api\Service\PdkActionsService;
use MyParcelNL\Pdk\App\Cart\Contract\CartCalculationServiceInterface;
use MyParcelNL\Pdk\App\Cart\Service\CartCalculationService;
use MyParcelNL\Pdk\App\DeliveryOptions\Contract\DeliveryOptionsFeesServiceInterface;
Expand Down Expand Up @@ -183,6 +185,11 @@
return \MyParcelNL\Pdk\Facade\Pdk::get(AccountRepositoryInterface::class);
}),

/**
* Handles executing pdk actions.
*/
PdkActionsServiceInterface::class => autowire(PdkActionsService::class),

/**
* Handles order options calculation.
*/
Expand Down
27 changes: 27 additions & 0 deletions src/App/Api/Contract/PdkActionsServiceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace MyParcelNL\Pdk\App\Api\Contract;

use Symfony\Component\HttpFoundation\Response;

interface PdkActionsServiceInterface
{
/**
* @param string|\Symfony\Component\HttpFoundation\Request $action
* @param array $parameters
*
* @return \Symfony\Component\HttpFoundation\Response
* @throws \MyParcelNL\Pdk\Api\Exception\ApiException
* @throws \MyParcelNL\Pdk\Api\Exception\PdkEndpointException
*/
public function execute($action, array $parameters = []): Response;

/**
* @param string $context
*
* @return $this
*/
public function setContext(string $context): self;
}
106 changes: 5 additions & 101 deletions src/App/Api/PdkActions.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,107 +5,11 @@

namespace MyParcelNL\Pdk\App\Api;

use InvalidArgumentException;
use MyParcelNL\Pdk\Api\Exception\PdkEndpointException;
use MyParcelNL\Pdk\Base\Support\Collection;
use MyParcelNL\Pdk\Facade\Config;
use MyParcelNL\Pdk\Facade\Pdk;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use MyParcelNL\Pdk\App\Api\Service\PdkActionsService;

class PdkActions
/**
* @deprecated use PdkActionsService. Will be removed in v3.0.0.
*/
class PdkActions extends PdkActionsService
{
/**
* @var string
*/
private $context = PdkEndpoint::CONTEXT_BACKEND;

/**
* @param string|\Symfony\Component\HttpFoundation\Request $action
* @param array $parameters
*
* @return \Symfony\Component\HttpFoundation\Response
* @throws \MyParcelNL\Pdk\Api\Exception\ApiException
* @throws \MyParcelNL\Pdk\Api\Exception\PdkEndpointException
*/
public function execute($action, array $parameters = []): Response
{
$request = $this->createRequest($action, $parameters);

$actionClass = $this->resolveAction($request);

/** @var \MyParcelNL\Pdk\App\Action\Contract\ActionInterface $action */
$action = Pdk::get($actionClass);

return $action->handle($request);
}

/**
* @param string $context
*
* @return $this
*/
public function setContext(string $context): self
{
$this->context = $context;

return $this;
}

/**
* @param $input
* @param array $parameters
*
* @return \Symfony\Component\HttpFoundation\Request
*/
protected function createRequest($input, array $parameters = []): Request
{
if ($input instanceof Request) {
if (empty($input->get('action'))) {
throw new InvalidArgumentException('Required parameter "action" is missing.');
}

return $input;
}

if (is_string($input)) {
$request = Request::createFromGlobals();

foreach ($parameters as $key => $value) {
$request->query->set($key, $value);
}

$request->query->set('action', $input);

return $request;
}

throw new InvalidArgumentException('Input must be a string or a Request object.');
}

/**
* @param \Symfony\Component\HttpFoundation\Request $request
*
* @return string
* @throws \MyParcelNL\Pdk\Api\Exception\PdkEndpointException
*/
private function resolveAction(Request $request): string
{
$action = $request->get('action');

if (! $this->context || ! in_array($this->context, PdkEndpoint::CONTEXTS, true)) {
throw new PdkEndpointException('Context is invalid.', Response::HTTP_UNPROCESSABLE_ENTITY);
}

$actions = new Collection(Config::get('actions'));

$match = $actions->dataGet("$this->context.$action") ?? $actions->dataGet("shared.$action");
$actionClass = $match['action'] ?? null;

if (! $actionClass || ! class_exists($actionClass)) {
throw new PdkEndpointException("Action \"$action\" does not exist.", Response::HTTP_UNPROCESSABLE_ENTITY);
}

return $actionClass;
}
}
112 changes: 112 additions & 0 deletions src/App/Api/Service/PdkActionsService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

declare(strict_types=1);

namespace MyParcelNL\Pdk\App\Api\Service;

use InvalidArgumentException;
use MyParcelNL\Pdk\Api\Exception\PdkEndpointException;
use MyParcelNL\Pdk\App\Api\Contract\PdkActionsServiceInterface;
use MyParcelNL\Pdk\App\Api\PdkEndpoint;
use MyParcelNL\Pdk\Base\Support\Collection;
use MyParcelNL\Pdk\Facade\Config;
use MyParcelNL\Pdk\Facade\Pdk;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class PdkActionsService implements PdkActionsServiceInterface
{
/**
* @var string
*/
private $context = PdkEndpoint::CONTEXT_BACKEND;

/**
* @param string|\Symfony\Component\HttpFoundation\Request $action
* @param array $parameters
*
* @return \Symfony\Component\HttpFoundation\Response
* @throws \MyParcelNL\Pdk\Api\Exception\ApiException
* @throws \MyParcelNL\Pdk\Api\Exception\PdkEndpointException
*/
public function execute($action, array $parameters = []): Response
{
$request = $this->createRequest($action, $parameters);

$actionClass = $this->resolveAction($request);

/** @var \MyParcelNL\Pdk\App\Action\Contract\ActionInterface $action */
$action = Pdk::get($actionClass);

return $action->handle($request);
}

/**
* @param string $context
*
* @return $this
*/
public function setContext(string $context): PdkActionsServiceInterface
{
$this->context = $context;

return $this;
}

/**
* @param $input
* @param array $parameters
*
* @return \Symfony\Component\HttpFoundation\Request
*/
protected function createRequest($input, array $parameters = []): Request
{
if ($input instanceof Request) {
if (empty($input->get('action'))) {
throw new InvalidArgumentException('Required parameter "action" is missing.');
}

return $input;
}

if (is_string($input)) {
$request = Request::createFromGlobals();

foreach ($parameters as $key => $value) {
$request->query->set($key, $value);
}

$request->query->set('action', $input);

return $request;
}

throw new InvalidArgumentException('Input must be a string or a Request object.');
}

/**
* @param \Symfony\Component\HttpFoundation\Request $request
*
* @return string
* @throws \MyParcelNL\Pdk\Api\Exception\PdkEndpointException
*/
private function resolveAction(Request $request): string
{
$action = $request->get('action');

if (! $this->context || ! in_array($this->context, PdkEndpoint::CONTEXTS, true)) {
throw new PdkEndpointException('Context is invalid.', Response::HTTP_UNPROCESSABLE_ENTITY);
}

$actions = new Collection(Config::get('actions'));

$match = $actions->dataGet("$this->context.$action") ?? $actions->dataGet("shared.$action");
$actionClass = $match['action'] ?? null;

if (! $actionClass || ! class_exists($actionClass)) {
throw new PdkEndpointException("Action \"$action\" does not exist.", Response::HTTP_UNPROCESSABLE_ENTITY);
}

return $actionClass;
}
}
6 changes: 3 additions & 3 deletions src/Facade/Actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@

namespace MyParcelNL\Pdk\Facade;

use MyParcelNL\Pdk\App\Api\PdkActions;
use MyParcelNL\Pdk\App\Api\Service\PdkActionsService;
use MyParcelNL\Pdk\Base\Facade;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* @method static Response execute(string|Request $action, array $parameters = [])
* @see PdkActions
* @see PdkActionsService
*/
final class Actions extends Facade
{
protected static function getFacadeAccessor(): string
{
return PdkActions::class;
return PdkActionsService::class;
}
}
54 changes: 54 additions & 0 deletions tests/Bootstrap/MockPdkActionsService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace MyParcelNL\Pdk\Tests\Bootstrap;

use MyParcelNL\Pdk\App\Api\Service\PdkActionsService;
use MyParcelNL\Pdk\Base\Support\Collection;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Service\ResetInterface;

final class MockPdkActionsService extends PdkActionsService implements ResetInterface
{
/**
* @var \MyParcelNL\Pdk\Base\Support\Collection
*/
private $calls;

public function __construct()
{
$this->reset();
}

/**
* @param string|\Symfony\Component\HttpFoundation\Request $action
* @param array $parameters
*
* @return \Symfony\Component\HttpFoundation\Response
* @throws \MyParcelNL\Pdk\Api\Exception\ApiException
* @throws \MyParcelNL\Pdk\Api\Exception\PdkEndpointException
*/
public function execute($action, array $parameters = []): Response
{
$this->calls->push([
'action' => $action,
'parameters' => $parameters,
]);

return parent::execute($action, $parameters);
}

/**
* @return \MyParcelNL\Pdk\Base\Support\Collection
*/
public function getCalls(): Collection
{
return $this->calls;
}

public function reset(): void
{
$this->calls = new Collection();
}
}
2 changes: 2 additions & 0 deletions tests/Bootstrap/MockPdkConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use MyParcelNL\Pdk\App\Account\Contract\PdkAccountRepositoryInterface;
use MyParcelNL\Pdk\App\Api\Contract\BackendEndpointServiceInterface;
use MyParcelNL\Pdk\App\Api\Contract\FrontendEndpointServiceInterface;
use MyParcelNL\Pdk\App\Api\Contract\PdkActionsServiceInterface;
use MyParcelNL\Pdk\App\Cart\Contract\PdkCartRepositoryInterface;
use MyParcelNL\Pdk\App\Installer\Contract\InstallerServiceInterface;
use MyParcelNL\Pdk\App\Installer\Contract\MigrationServiceInterface;
Expand Down Expand Up @@ -84,6 +85,7 @@ private static function getDefaultConfig(): array
MigrationServiceInterface::class => get(MockMigrationService::class),
OrderStatusServiceInterface::class => get(MockOrderStatusService::class),
PdkAccountRepositoryInterface::class => get(MockPdkAccountRepository::class),
PdkActionsServiceInterface::class => get(MockPdkActionsService::class),
PdkCartRepositoryInterface::class => get(MockPdkCartRepository::class),
PdkInterface::class => get(MockPdk::class),
PdkOrderNoteRepositoryInterface::class => get(MockPdkOrderNoteRepository::class),
Expand Down
1 change: 1 addition & 0 deletions tests/Bootstrap/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ protected function getResetServices(): array
MockCarrierSchema::class,
MockMemoryCacheStorage::class,
MockOrderStatusService::class,
MockPdkActionsService::class,
SharedFactoryState::class,
];
}
Expand Down
Loading

0 comments on commit 94508ee

Please sign in to comment.