Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webhooks): link shipment to order #327

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/App/Action/Backend/Shipment/UpdateShipmentsAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ public function handle(Request $request): Response
$orders = $this->pdkOrderRepository->getMany($this->getOrderIds($request));
$shipments = $this->shipmentRepository->getShipments($this->getShipmentIds($request, $orders));

if ($request->get('linkFirstShipmentToFirstOrder')
&& $orders->isNotEmpty()
&& $shipments->isNotEmpty()
) {
$shipments->first()->orderId = $orders->first()->getExternalIdentifier();
}

if ($orders->isNotEmpty()) {
$orders->updateShipments($shipments);
$this->pdkOrderRepository->updateMany($orders);
Expand Down Expand Up @@ -103,4 +110,3 @@ private function addBarcodeNotes(ShipmentCollection $shipments): void
});
}
}

joerivanveen marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion src/App/Order/Collection/PdkOrderCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ private function mergeShipmentsById(ShipmentCollection $shipments, PdkOrder $ord
return $orderShipments->values();
}


/**
* @param \MyParcelNL\Pdk\Shipment\Collection\ShipmentCollection $shipments
* @param \MyParcelNL\Pdk\App\Order\Model\PdkOrder $order
Expand All @@ -170,4 +171,3 @@ private function mergeShipmentsByOrder(ShipmentCollection $shipments, PdkOrder $
return $merged;
}
}

joerivanveen marked this conversation as resolved.
Show resolved Hide resolved
21 changes: 21 additions & 0 deletions src/App/Order/Repository/AbstractPdkOrderRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use MyParcelNL\Pdk\App\Order\Model\PdkOrder;
use MyParcelNL\Pdk\Base\Repository\Repository;
use MyParcelNL\Pdk\Base\Support\Utils;
use MyParcelNL\Pdk\Facade\Logger;

abstract class AbstractPdkOrderRepository extends Repository implements PdkOrderRepositoryInterface
{
Expand All @@ -19,6 +20,25 @@ abstract class AbstractPdkOrderRepository extends Repository implements PdkOrder
*/
abstract public function get($input): PdkOrder;

/**
* TODO: v3.0.0 make method abstract to force implementation
*
* @param string $uuid
*
* @return null|\MyParcelNL\Pdk\App\Order\Model\PdkOrder
*/
public function getByApiIdentifier(string $uuid): ?PdkOrder
FreekVR marked this conversation as resolved.
Show resolved Hide resolved
{
Logger::notice(
'Implement getByApiIdentifier, in PDK v3 it will be required.',
[
'class' => self::class,
]
);

return $this->get(['order_id' => $uuid]);
}

/**
* @param string|string[] $orderIds
*
Expand All @@ -39,6 +59,7 @@ public function update(PdkOrder $order): PdkOrder
return $this->save($order->externalIdentifier, $order);
}


/**
* @param \MyParcelNL\Pdk\App\Order\Collection\PdkOrderCollection $collection
*
Expand Down
17 changes: 13 additions & 4 deletions src/App/Webhook/Hook/ShipmentStatusChangeWebhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace MyParcelNL\Pdk\App\Webhook\Hook;

use MyParcelNL\Pdk\App\Api\Backend\PdkBackendActions;
use MyParcelNL\Pdk\App\Order\Contract\PdkOrderRepositoryInterface;
use MyParcelNL\Pdk\Facade\Actions;
use MyParcelNL\Pdk\Facade\Pdk;
use MyParcelNL\Pdk\Webhook\Model\WebhookSubscription;
use Symfony\Component\HttpFoundation\Request;

Expand All @@ -20,12 +22,19 @@ public function handle(Request $request): void
{
$content = $this->getHookBody($request);

Actions::execute(PdkBackendActions::UPDATE_SHIPMENTS, [
'orderIds' => [$content['shipment_reference_identifier']],
'shipmentIds' => [$content['shipment_id']],
]);
// translate order_id (which is api identifier / uuid) to local order id for db
$order = Pdk::get(PdkOrderRepositoryInterface::class)->getByApiIdentifier($content['order_id']);
FreekVR marked this conversation as resolved.
Show resolved Hide resolved

if ($order) {
Actions::execute(PdkBackendActions::UPDATE_SHIPMENTS, [
'orderIds' => [$order->getExternalIdentifier()],
'shipmentIds' => [$content['shipment_id']],
'linkFirstShipmentToFirstOrder' => true,
]);
}
}


/**
* @return string
*/
Expand Down
5 changes: 5 additions & 0 deletions tests/Bootstrap/MockPdkOrderRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public function get($input): PdkOrder
});
}

public function getByApiIdentifier(string $uuid): ?PdkOrder
{
return new PdkOrder(['externalIdentifier' => 197]);
}

protected function getKeyPrefix(): string
{
return static::class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
use MyParcelNL\Pdk\App\Order\Contract\PdkOrderRepositoryInterface;
use MyParcelNL\Pdk\App\Order\Model\PdkOrder;
use MyParcelNL\Pdk\Facade\Pdk;
use MyParcelNL\Pdk\Storage\Contract\StorageInterface;
use MyParcelNL\Pdk\Tests\Uses\UsesMockPdkInstance;
use Psr\Log\LoggerInterface;
use function MyParcelNL\Pdk\Tests\usesShared;

usesShared(new UsesMockPdkInstance());
Expand Down Expand Up @@ -44,3 +46,33 @@

expect($newOrder)->toBeInstanceOf(PdkOrder::class);
});

it('gets order by api identifier', function () {
/** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockLogger $logger */
$logger = Pdk::get(LoggerInterface::class);
class MockPdkOrderRepository extends AbstractPdkOrderRepository
{
public function get($input): PdkOrder
{
return new PdkOrder();
}
}
$repository = new MockPdkOrderRepository(Pdk::get(StorageInterface::class));
$order = $repository->getByApiIdentifier('123');

expect($order)
->toBeInstanceOf(PdkOrder::class)
->and($logger->getLogs())
->toEqual([
[
'level' => 'notice',
'message' => '[PDK]: Implement getByApiIdentifier, in PDK v3 it will be required.',
'context' =>
[
'class' => 'MyParcelNL\\Pdk\\App\\Order\\Repository\\AbstractPdkOrderRepository',
],
],
]
);
});
joerivanveen marked this conversation as resolved.
Show resolved Hide resolved

96 changes: 96 additions & 0 deletions tests/Unit/App/Webhook/Hook/ShipmentStatusChangeWebhookTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
/** @noinspection StaticClosureCanBeUsedInspection */

declare(strict_types=1);

namespace MyParcelNL\Pdk\App\Webhook\Hook;

use MyParcelNL\Pdk\App\Webhook\Contract\PdkWebhookManagerInterface;
use MyParcelNL\Pdk\App\Webhook\Contract\PdkWebhooksRepositoryInterface;
use MyParcelNL\Pdk\Base\Contract\CronServiceInterface;
use MyParcelNL\Pdk\Base\Support\Collection;
use MyParcelNL\Pdk\Facade\Pdk;
use MyParcelNL\Pdk\Tests\Api\Response\ExampleGetShipmentsResponse;
use MyParcelNL\Pdk\Tests\Bootstrap\MockApi;
use MyParcelNL\Pdk\Tests\Uses\UsesMockEachCron;
use MyParcelNL\Pdk\Tests\Uses\UsesMockEachLogger;
use MyParcelNL\Pdk\Tests\Uses\UsesMockPdkInstance;
use MyParcelNL\Pdk\Webhook\Collection\WebhookSubscriptionCollection;
use MyParcelNL\Pdk\Webhook\Model\WebhookSubscription;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use function MyParcelNL\Pdk\Tests\usesShared;

uses()->group('webhook');

usesShared(new UsesMockPdkInstance(), new UsesMockEachCron(), new UsesMockEachLogger());

it('handles an api request', function (string $hook, string $expectedClass, array $hookBody) {
/** @var PdkWebhooksRepositoryInterface $repository */
$repository = Pdk::get(PdkWebhooksRepositoryInterface::class);
/** @var PdkWebhookManagerInterface $webhookManager */
$webhookManager = Pdk::get(PdkWebhookManagerInterface::class);
/** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockCronService $cronService */
$cronService = Pdk::get(CronServiceInterface::class);
/** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockLogger $logger */
$logger = Pdk::get(LoggerInterface::class);

$repository->storeHashedUrl('https://example.com/hook/1234567890abcdef');
$repository->store(new WebhookSubscriptionCollection([['hook' => $hook, 'url' => $repository->getHashedUrl()]]));
MockApi::enqueue(new ExampleGetShipmentsResponse());

$request = Request::create(
$repository->getHashedUrl(),
Request::METHOD_POST,
[],
[],
[],
['HTTP_X_MYPARCEL_HOOK' => $hook],
json_encode([
'data' => [
'hooks' => [
array_merge(['event' => $hook], $hookBody),
],
],
])
);

$webhookManager->call($request);
$cronService->executeScheduledTask();

$logs = (new Collection($logger->getLogs()))->map(function (array $log) {
// Omit the request from the logs.
unset($log['context']['request']);
return $log;
});
// Omit the shipment response from the logs.
unset($logs[1]);

expect(array_values($logs->toArray()))->toBe([
[
'level' => 'debug',
'message' => '[PDK]: Webhook received',
'context' => [],
],
[
'level' => 'debug',
'message' => '[PDK]: Webhook processed',
'context' => ['hook' => $expectedClass],
],
]);
})->with([
'shipment updated' => [
'hook' => WebhookSubscription::SHIPMENT_STATUS_CHANGE,
'class' => ShipmentStatusChangeWebhook::class,
'body' => [
'shipment_id' => 192031595,
'account_id' => 162450,
'order_id' => 'api-uuid-string',
'shop_id' => 83287,
'status' => 2,
'barcode' => '3SHOHR763563926',
'shipment_reference_identifier' => '',
],
],
]);
joerivanveen marked this conversation as resolved.
Show resolved Hide resolved