Skip to content

Commit

Permalink
Handle webhooks
Browse files Browse the repository at this point in the history
  • Loading branch information
loevgaard committed Jun 17, 2024
1 parent 1298f82 commit 08c2cc3
Show file tree
Hide file tree
Showing 27 changed files with 487 additions and 188 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
```

## Important

The plugin presumes that the `Product::$code` and `ProductVariant::$code` are the same the product id and variant id
respectively inside Peak WMS. Also, because Sylius _always_ has a product and a variant, it is presumed that Peak WMS
uses the same logic.

## Development

```shell
Expand Down
37 changes: 37 additions & 0 deletions src/Controller/HandleWebhookControllerAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusPeakWMSPlugin\Controller;

use Setono\PeakWMS\Parser\WebhookParser;
use Setono\PeakWMS\Parser\WebhookParserInterface;
use Setono\SyliusPeakWMSPlugin\WebhookHandler\WebhookHandlerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

final class HandleWebhookControllerAction
{
public function __construct(

Check warning on line 17 in src/Controller/HandleWebhookControllerAction.php

View check run for this annotation

Codecov / codecov/patch

src/Controller/HandleWebhookControllerAction.php#L17

Added line #L17 was not covered by tests
private readonly WebhookParserInterface $webhookParser,
private readonly WebhookHandlerInterface $webhookHandler,
) {
}

public function __invoke(Request $request): JsonResponse

Check warning on line 23 in src/Controller/HandleWebhookControllerAction.php

View check run for this annotation

Codecov / codecov/patch

src/Controller/HandleWebhookControllerAction.php#L23

Added line #L23 was not covered by tests
{
try {
$dataClass = WebhookParser::convertNameToDataClass($request->query->getInt('name'));

Check warning on line 26 in src/Controller/HandleWebhookControllerAction.php

View check run for this annotation

Codecov / codecov/patch

src/Controller/HandleWebhookControllerAction.php#L26

Added line #L26 was not covered by tests

$data = $this->webhookParser->parse($request->getContent(), $dataClass);

Check warning on line 28 in src/Controller/HandleWebhookControllerAction.php

View check run for this annotation

Codecov / codecov/patch

src/Controller/HandleWebhookControllerAction.php#L28

Added line #L28 was not covered by tests

$this->webhookHandler->handle($data);
} catch (\InvalidArgumentException $e) {
throw new BadRequestHttpException($e->getMessage());

Check warning on line 32 in src/Controller/HandleWebhookControllerAction.php

View check run for this annotation

Codecov / codecov/patch

src/Controller/HandleWebhookControllerAction.php#L30-L32

Added lines #L30 - L32 were not covered by tests
}

return new JsonResponse(status: Response::HTTP_NO_CONTENT);

Check warning on line 35 in src/Controller/HandleWebhookControllerAction.php

View check run for this annotation

Codecov / codecov/patch

src/Controller/HandleWebhookControllerAction.php#L35

Added line #L35 was not covered by tests
}
}
19 changes: 0 additions & 19 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
namespace Setono\SyliusPeakWMSPlugin\DependencyInjection;

use Setono\SyliusPeakWMSPlugin\Model\RegisteredWebhooks;
use Setono\SyliusPeakWMSPlugin\Model\RemoteEvent;
use Setono\SyliusPeakWMSPlugin\Model\UploadOrderRequest;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Bundle\ResourceBundle\Form\Type\DefaultResourceType;
use Sylius\Component\Resource\Factory\Factory;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
Expand Down Expand Up @@ -59,22 +56,6 @@ private function addResourcesSection(ArrayNodeDefinition $node): void
->end()
->end()
->end()
->arrayNode('remote_event')
->addDefaultsIfNotSet()
->children()
->variableNode('options')->end()
->arrayNode('classes')
->addDefaultsIfNotSet()
->children()
->scalarNode('model')->defaultValue(RemoteEvent::class)->cannotBeEmpty()->end()
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
->scalarNode('repository')->cannotBeEmpty()->end()
->scalarNode('form')->defaultValue(DefaultResourceType::class)->end()
->scalarNode('factory')->defaultValue(Factory::class)->end()
->end()
->end()
->end()
->end()
->arrayNode('upload_order_request')
->addDefaultsIfNotSet()
->children()
Expand Down
6 changes: 6 additions & 0 deletions src/DependencyInjection/SetonoSyliusPeakWMSExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Setono\SyliusPeakWMSPlugin\DependencyInjection;

use Setono\SyliusPeakWMSPlugin\DataMapper\SalesOrderDataMapperInterface;
use Setono\SyliusPeakWMSPlugin\WebhookHandler\WebhookHandlerInterface;
use Setono\SyliusPeakWMSPlugin\Workflow\UploadOrderRequestWorkflow;
use Sylius\Bundle\ResourceBundle\DependencyInjection\Extension\AbstractResourceExtension;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
Expand All @@ -30,6 +31,11 @@ public function load(array $configs, ContainerBuilder $container): void
->addTag('setono_sylius_peak_wms.sales_order_data_mapper')
;

$container
->registerForAutoconfiguration(WebhookHandlerInterface::class)
->addTag('setono_sylius_peak_wms.webhook_handler')
;

$container->setParameter('setono_sylius_peak_wms.api_key', $config['api_key']);

$this->registerResources(
Expand Down
13 changes: 13 additions & 0 deletions src/Exception/UnsupportedWebhookException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusPeakWMSPlugin\Exception;

final class UnsupportedWebhookException extends \RuntimeException
{
public static function fromData(object $data): self

Check warning on line 9 in src/Exception/UnsupportedWebhookException.php

View check run for this annotation

Codecov / codecov/patch

src/Exception/UnsupportedWebhookException.php#L9

Added line #L9 was not covered by tests
{
return new self('Unsupported webhook: ' . $data::class);

Check warning on line 11 in src/Exception/UnsupportedWebhookException.php

View check run for this annotation

Codecov / codecov/patch

src/Exception/UnsupportedWebhookException.php#L11

Added line #L11 was not covered by tests
}
}
32 changes: 0 additions & 32 deletions src/Factory/RemoteEventFactory.php

This file was deleted.

18 changes: 0 additions & 18 deletions src/Factory/RemoteEventFactoryInterface.php

This file was deleted.

24 changes: 24 additions & 0 deletions src/Message/Command/UpdateInventory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusPeakWMSPlugin\Message\Command;

use Sylius\Component\Core\Model\ProductVariantInterface;

/**
* Will update the inventory for a product variant
*/
final class UpdateInventory implements CommandInterface
{
public int $productVariant;

public function __construct(int|ProductVariantInterface $productVariant)

Check warning on line 16 in src/Message/Command/UpdateInventory.php

View check run for this annotation

Codecov / codecov/patch

src/Message/Command/UpdateInventory.php#L16

Added line #L16 was not covered by tests
{
if ($productVariant instanceof ProductVariantInterface) {
$productVariant = (int) $productVariant->getId();

Check warning on line 19 in src/Message/Command/UpdateInventory.php

View check run for this annotation

Codecov / codecov/patch

src/Message/Command/UpdateInventory.php#L18-L19

Added lines #L18 - L19 were not covered by tests
}

$this->productVariant = $productVariant;

Check warning on line 22 in src/Message/Command/UpdateInventory.php

View check run for this annotation

Codecov / codecov/patch

src/Message/Command/UpdateInventory.php#L22

Added line #L22 was not covered by tests
}
}
61 changes: 61 additions & 0 deletions src/Message/CommandHandler/UpdateInventoryHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusPeakWMSPlugin\Message\CommandHandler;

use Setono\PeakWMS\Client\ClientInterface;
use Setono\PeakWMS\DataTransferObject\Product\Product;
use Setono\SyliusPeakWMSPlugin\Message\Command\UpdateInventory;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;

final class UpdateInventoryHandler
{
public function __construct(

Check warning on line 16 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L16

Added line #L16 was not covered by tests
private readonly ClientInterface $client,
private readonly ProductVariantRepositoryInterface $productVariantRepository,
) {
}

public function __invoke(UpdateInventory $message): void

Check warning on line 22 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L22

Added line #L22 was not covered by tests
{
$productVariant = $this->productVariantRepository->find($message->productVariant);
if (!$productVariant instanceof ProductVariantInterface) {
throw new UnrecoverableMessageHandlingException(sprintf('Product variant with id %d not found', $message->productVariant));

Check warning on line 26 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L24-L26

Added lines #L24 - L26 were not covered by tests
}

$productCode = $productVariant->getProduct()?->getCode();
$variantCode = $productVariant->getCode();

Check warning on line 30 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L29-L30

Added lines #L29 - L30 were not covered by tests

if (null === $productCode || null === $variantCode) {
throw new UnrecoverableMessageHandlingException(sprintf('Product variant with id %d does not have a product code or variant code', $message->productVariant));

Check warning on line 33 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L32-L33

Added lines #L32 - L33 were not covered by tests
}

$collection = $this
->client
->product()
->getByProductId($productCode)
->filter(fn (Product $product) => $product->variantId === $variantCode)
;

Check warning on line 41 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L36-L41

Added lines #L36 - L41 were not covered by tests

if (count($collection) === 0) {
throw new UnrecoverableMessageHandlingException(sprintf('The product with id %s does not have a variant with id/code %s', $productCode, $variantCode));

Check warning on line 44 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L43-L44

Added lines #L43 - L44 were not covered by tests
}

if (count($collection) > 1) {
throw new UnrecoverableMessageHandlingException(sprintf('The product with id %s has multiple products with the same variant id/code', $productCode));

Check warning on line 48 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L47-L48

Added lines #L47 - L48 were not covered by tests
}

$peakProduct = $collection[0];

Check warning on line 51 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L51

Added line #L51 was not covered by tests

if (null === $peakProduct->availableToSell) {
throw new UnrecoverableMessageHandlingException(sprintf('The product with id %s and variant id/code %s does not have an availableToSell value', $productCode, $variantCode));

Check warning on line 54 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L53-L54

Added lines #L53 - L54 were not covered by tests
}

$productVariant->setOnHand($peakProduct->availableToSell);

Check warning on line 57 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L57

Added line #L57 was not covered by tests

$this->productVariantRepository->add($productVariant);

Check warning on line 59 in src/Message/CommandHandler/UpdateInventoryHandler.php

View check run for this annotation

Codecov / codecov/patch

src/Message/CommandHandler/UpdateInventoryHandler.php#L59

Added line #L59 was not covered by tests
}
}
63 changes: 0 additions & 63 deletions src/Model/RemoteEvent.php

This file was deleted.

29 changes: 0 additions & 29 deletions src/Model/RemoteEventInterface.php

This file was deleted.

39 changes: 39 additions & 0 deletions src/Provider/ProductVariantProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusPeakWMSPlugin\Provider;

use Doctrine\Persistence\ManagerRegistry;
use Setono\Doctrine\ORMTrait;
use Setono\PeakWMS\DataTransferObject\Webhook\WebhookDataStockAdjust;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Webmozart\Assert\Assert;

final class ProductVariantProvider implements ProductVariantProviderInterface
{
use ORMTrait;

public function __construct(

Check warning on line 17 in src/Provider/ProductVariantProvider.php

View check run for this annotation

Codecov / codecov/patch

src/Provider/ProductVariantProvider.php#L17

Added line #L17 was not covered by tests
ManagerRegistry $managerRegistry,
/** @var class-string<ProductVariantInterface> $productVariantClass */
private readonly string $productVariantClass,
) {
$this->managerRegistry = $managerRegistry;

Check warning on line 22 in src/Provider/ProductVariantProvider.php

View check run for this annotation

Codecov / codecov/patch

src/Provider/ProductVariantProvider.php#L22

Added line #L22 was not covered by tests
}

public function provideFromStockAdjustment(WebhookDataStockAdjust $stockAdjustment): ?ProductVariantInterface

Check failure on line 25 in src/Provider/ProductVariantProvider.php

View workflow job for this annotation

GitHub Actions / Static Code Analysis (PHP8.2 | Deps: lowest | SF~6.4.0)

MixedInferredReturnType

src/Provider/ProductVariantProvider.php:25:90: MixedInferredReturnType: Could not verify return type 'Sylius\Component\Core\Model\ProductVariantInterface|null' for Setono\SyliusPeakWMSPlugin\Provider\ProductVariantProvider::provideFromStockAdjustment (see https://psalm.dev/047)

Check failure on line 25 in src/Provider/ProductVariantProvider.php

View workflow job for this annotation

GitHub Actions / Static Code Analysis (PHP8.1 | Deps: lowest | SF~6.4.0)

MixedInferredReturnType

src/Provider/ProductVariantProvider.php:25:90: MixedInferredReturnType: Could not verify return type 'Sylius\Component\Core\Model\ProductVariantInterface|null' for Setono\SyliusPeakWMSPlugin\Provider\ProductVariantProvider::provideFromStockAdjustment (see https://psalm.dev/047)

Check warning on line 25 in src/Provider/ProductVariantProvider.php

View check run for this annotation

Codecov / codecov/patch

src/Provider/ProductVariantProvider.php#L25

Added line #L25 was not covered by tests
{
$obj = $this

Check failure on line 27 in src/Provider/ProductVariantProvider.php

View workflow job for this annotation

GitHub Actions / Static Code Analysis (PHP8.2 | Deps: lowest | SF~6.4.0)

MixedAssignment

src/Provider/ProductVariantProvider.php:27:9: MixedAssignment: Unable to determine the type that $obj is being assigned to (see https://psalm.dev/032)

Check failure on line 27 in src/Provider/ProductVariantProvider.php

View workflow job for this annotation

GitHub Actions / Static Code Analysis (PHP8.1 | Deps: lowest | SF~6.4.0)

MixedAssignment

src/Provider/ProductVariantProvider.php:27:9: MixedAssignment: Unable to determine the type that $obj is being assigned to (see https://psalm.dev/032)
->getRepository($this->productVariantClass)
->createQueryBuilder('o')
->where('o.code = :code')
->setParameter('code', $stockAdjustment->variantId)
->getQuery()
->getOneOrNullResult()
;
Assert::nullOrIsInstanceOf($obj, ProductVariantInterface::class);

Check warning on line 35 in src/Provider/ProductVariantProvider.php

View check run for this annotation

Codecov / codecov/patch

src/Provider/ProductVariantProvider.php#L27-L35

Added lines #L27 - L35 were not covered by tests

return $obj;

Check failure on line 37 in src/Provider/ProductVariantProvider.php

View workflow job for this annotation

GitHub Actions / Static Code Analysis (PHP8.2 | Deps: lowest | SF~6.4.0)

MixedReturnStatement

src/Provider/ProductVariantProvider.php:37:16: MixedReturnStatement: Could not infer a return type (see https://psalm.dev/138)

Check failure on line 37 in src/Provider/ProductVariantProvider.php

View workflow job for this annotation

GitHub Actions / Static Code Analysis (PHP8.1 | Deps: lowest | SF~6.4.0)

MixedReturnStatement

src/Provider/ProductVariantProvider.php:37:16: MixedReturnStatement: Could not infer a return type (see https://psalm.dev/138)

Check warning on line 37 in src/Provider/ProductVariantProvider.php

View check run for this annotation

Codecov / codecov/patch

src/Provider/ProductVariantProvider.php#L37

Added line #L37 was not covered by tests
}
}
Loading

0 comments on commit 08c2cc3

Please sign in to comment.