Skip to content
Permalink
Browse files Browse the repository at this point in the history
NEXT-15183 - Add Dangerfile
  • Loading branch information
shyim authored and philipgatzka committed Jun 8, 2021
1 parent 5a5f000 commit b5c3ce3
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 6 deletions.
8 changes: 8 additions & 0 deletions .gitlab-ci.yml
Expand Up @@ -59,6 +59,14 @@ default:

# stage: unit

Danger:
stage: unit
image: node:14
before_script: []
script:
- npm install danger gitlab
- node node_modules/.bin/danger ci --base trunk

PHP Full:
image: $DEV_IMAGE
stage: unit
Expand Down
54 changes: 54 additions & 0 deletions dangerfile.js
@@ -0,0 +1,54 @@
const GitlabClient = require('gitlab');

const api = new GitlabClient.Gitlab({
token: process.env['DANGER_GITLAB_API_TOKEN'],
host: process.env['DANGER_GITLAB_HOST']
});

const addLabel = (labels) => {
const currentLabels = danger.gitlab.mr.labels;

for (const label of labels) {
currentLabels.push(label);
}

api.MergeRequests.edit(1, process.env['CI_MERGE_REQUEST_IID'], {
'labels': currentLabels.join(',')
})
};

const removeLabel = (labels) => {
const currentLabels = danger.gitlab.mr.labels;

for (const label of labels) {
const index = currentLabels.indexOf(label);
if (index > -1) {
currentLabels.splice(index, 1);
}

api.MergeRequests.edit(1, process.env['CI_MERGE_REQUEST_IID'], {
'labels': currentLabels.join(',')
})
}
};

const hasStoreApiRouteChanges = () => {
for (let file of danger.git.modified_files) {
if (file.includes('SalesChannel') && file.includes('Route.php') && !file.includes('/Test/')) {
return true;
}
}

for (let file of danger.git.created_files) {
if (file.includes('SalesChannel') && file.includes('Route.php') && !file.includes('/Test/')) {
return true;
}
}

return false;
}

if (hasStoreApiRouteChanges()) {
warn('Store-API Route has been modified. @Reviewers please review carefully!')
addLabel(['Security-Audit Required']);
}
1 change: 1 addition & 0 deletions src/Core/Checkout/DependencyInjection/order.xml
Expand Up @@ -95,6 +95,7 @@

<service id="Shopware\Core\Checkout\Order\SalesChannel\CancelOrderRoute" public="true">
<argument type="service" id="Shopware\Core\Checkout\Order\SalesChannel\OrderService"/>
<argument type="service" id="order.repository"/>
</service>

<service id="Shopware\Core\Checkout\Order\SalesChannel\SetPaymentOrderRoute" public="true">
Expand Down
40 changes: 34 additions & 6 deletions src/Core/Checkout/Order/SalesChannel/CancelOrderRoute.php
Expand Up @@ -3,10 +3,16 @@
namespace Shopware\Core\Checkout\Order\SalesChannel;

use OpenApi\Annotations as OA;
use Shopware\Core\Checkout\Cart\Exception\CustomerNotLoggedInException;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Exception\EntityNotFoundException;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
use Shopware\Core\Framework\Routing\Annotation\LoginRequired;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\Framework\Routing\Annotation\Since;
use Shopware\Core\Framework\Routing\Exception\InvalidRequestParameterException;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -17,14 +23,14 @@
*/
class CancelOrderRoute extends AbstractCancelOrderRoute
{
/**
* @var OrderService
*/
private $orderService;
private OrderService $orderService;

private EntityRepositoryInterface $orderRepository;

public function __construct(OrderService $orderService)
public function __construct(OrderService $orderService, EntityRepositoryInterface $orderRepository)
{
$this->orderService = $orderService;
$this->orderRepository = $orderRepository;
}

public function getDecorated(): AbstractCancelOrderRoute
Expand Down Expand Up @@ -56,13 +62,35 @@ public function getDecorated(): AbstractCancelOrderRoute
*/
public function cancel(Request $request, SalesChannelContext $context): CancelOrderRouteResponse
{
$orderId = $request->get('orderId', null);

if ($orderId === null) {
throw new InvalidRequestParameterException('orderId');
}

$this->verify($orderId, $context);

$newState = $this->orderService->orderStateTransition(
$request->get('orderId'),
$orderId,
'cancel',
new ParameterBag(),
$context->getContext()
);

return new CancelOrderRouteResponse($newState);
}

private function verify(string $orderId, SalesChannelContext $context): void
{
if ($context->getCustomer() === null) {
throw new CustomerNotLoggedInException();
}

$criteria = new Criteria([$orderId]);
$criteria->addFilter(new EqualsFilter('orderCustomer.customerId', $context->getCustomer()->getId()));

if ($this->orderRepository->searchIds($criteria, $context->getContext())->firstId() === null) {
throw new EntityNotFoundException('order', $orderId);
}
}
}
229 changes: 229 additions & 0 deletions src/Core/Checkout/Test/Order/SalesChannel/CancelOrderRouteTest.php
@@ -0,0 +1,229 @@
<?php declare(strict_types=1);

namespace Shopware\Core\Checkout\Test\Order\SalesChannel;

use PHPUnit\Framework\TestCase;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice;
use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice;
use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition;
use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection;
use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRuleCollection;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Checkout\Test\Customer\SalesChannel\CustomerTestTrait;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Test\IdsCollection;
use Shopware\Core\Framework\Test\TestCaseBase\IntegrationTestBehaviour;
use Shopware\Core\Framework\Test\TestDataCollection;
use Shopware\Core\Framework\Uuid\Uuid;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Component\HttpFoundation\Response;

class CancelOrderRouteTest extends TestCase
{
use IntegrationTestBehaviour;
use CustomerTestTrait;

private KernelBrowser $browser;

private EntityRepositoryInterface $customerRepository;

private IdsCollection $ids;

protected function setUp(): void
{
parent::setUp();

$this->customerRepository = $this->getContainer()->get('customer.repository');
$this->ids = new TestDataCollection();

$this->browser = $this->createCustomSalesChannelBrowser([
'id' => $this->ids->create('sales-channel'),
]);

$this->assignSalesChannelContext($this->browser);

$email = Uuid::randomHex() . '@example.com';
$customerId = $this->createCustomer('shopware', $email);

$this->ids->set('order-1', $this->createOrder($this->ids, $customerId));
$this->ids->set('order-2', $this->createOrder($this->ids, $this->createCustomer('test', 'test-other@test.de')));

$this->browser
->request(
'POST',
'/store-api/account/login',
[
'email' => $email,
'password' => 'shopware',
]
);

$response = json_decode($this->browser->getResponse()->getContent(), true);

$this->browser->setServerParameter('HTTP_SW_CONTEXT_TOKEN', $response['contextToken']);
}

public function testCancelMyOwnOrder(): void
{
$this->browser
->request(
'POST',
'/store-api/order/state/cancel',
[
'orderId' => $this->ids->get('order-1'),
]
);

$response = json_decode($this->browser->getResponse()->getContent(), true);

static::assertSame(Response::HTTP_OK, $this->browser->getResponse()->getStatusCode());
static::assertSame('cancelled', $response['technicalName']);

$criteria = new Criteria([$this->ids->get('order-1')]);
$criteria->addAssociation('stateMachineState');

/** @var OrderEntity $order */
$order = $this->getContainer()->get('order.repository')->search($criteria, Context::createDefaultContext())->first();

static::assertSame('cancelled', $order->getStateMachineState()->getTechnicalName());
}

public function testCancelRandomOrder(): void
{
$this->browser
->request(
'POST',
'/store-api/order/state/cancel',
[
'orderId' => Uuid::randomHex(),
]
);

$response = json_decode($this->browser->getResponse()->getContent(), true);

static::assertSame(Response::HTTP_NOT_FOUND, $this->browser->getResponse()->getStatusCode());
static::assertSame('FRAMEWORK__ENTITY_NOT_FOUND', $response['errors'][0]['code']);
}

public function testCancelOtherUsersOrder(): void
{
$this->browser
->request(
'POST',
'/store-api/order/state/cancel',
[
'orderId' => $this->ids->get('order-2'),
]
);

$response = json_decode($this->browser->getResponse()->getContent(), true);

static::assertSame(Response::HTTP_NOT_FOUND, $this->browser->getResponse()->getStatusCode());
static::assertSame('FRAMEWORK__ENTITY_NOT_FOUND', $response['errors'][0]['code']);
}

public function testCancelWithoutLogin(): void
{
$this->browser = $this->createCustomSalesChannelBrowser([
'id' => Uuid::randomHex(),
]);

$this->browser
->request(
'POST',
'/store-api/order/state/cancel',
[
'orderId' => $this->ids->get('order-2'),
]
);

$response = json_decode($this->browser->getResponse()->getContent(), true);

static::assertSame(Response::HTTP_FORBIDDEN, $this->browser->getResponse()->getStatusCode());
static::assertSame('CHECKOUT__CUSTOMER_NOT_LOGGED_IN', $response['errors'][0]['code']);
}

private function createOrder(IdsCollection $ids, string $customerId): string
{
$id = Uuid::randomHex();

$this->getContainer()->get('order.repository')->create(
[[
'id' => $id,
'orderDateTime' => (new \DateTimeImmutable())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
'price' => new CartPrice(10, 10, 10, new CalculatedTaxCollection(), new TaxRuleCollection(), CartPrice::TAX_STATE_NET),
'shippingCosts' => new CalculatedPrice(10, 10, new CalculatedTaxCollection(), new TaxRuleCollection()),
'orderCustomer' => [
'customerId' => $customerId,
'email' => 'test@example.com',
'salutationId' => $this->getValidSalutationId(),
'firstName' => 'Max',
'lastName' => 'Mustermann',
],
'stateId' => $this->getStateMachineState(),
'paymentMethodId' => $this->getValidPaymentMethodId(),
'currencyId' => Defaults::CURRENCY,
'currencyFactor' => 1.0,
'salesChannelId' => Defaults::SALES_CHANNEL,
'billingAddressId' => $billingAddressId = Uuid::randomHex(),
'addresses' => [
[
'id' => $billingAddressId,
'salutationId' => $this->getValidSalutationId(),
'firstName' => 'Max',
'lastName' => 'Mustermann',
'street' => 'Ebbinghoff 10',
'zipcode' => '48624',
'city' => 'Schöppingen',
'countryId' => $this->getValidCountryId(),
],
],
'lineItems' => [
[
'id' => $ids->get('VoucherA'),
'type' => LineItem::PROMOTION_LINE_ITEM_TYPE,
'code' => $ids->get('VoucherA'),
'identifier' => $ids->get('VoucherA'),
'quantity' => 1,
'payload' => ['promotionId' => $ids->get('voucherA')],
'label' => 'label',
'price' => new CalculatedPrice(200, 200, new CalculatedTaxCollection(), new TaxRuleCollection()),
'priceDefinition' => new QuantityPriceDefinition(200, new TaxRuleCollection(), 2),
],
[
'id' => $ids->get('VoucherC'),
'type' => LineItem::PROMOTION_LINE_ITEM_TYPE,
'code' => $ids->get('VoucherC'),
'identifier' => $ids->get('VoucherC'),
'payload' => ['promotionId' => $ids->get('voucherB')],
'quantity' => 1,
'label' => 'label',
'price' => new CalculatedPrice(200, 200, new CalculatedTaxCollection(), new TaxRuleCollection()),
'priceDefinition' => new QuantityPriceDefinition(200, new TaxRuleCollection(), 2),
],
[
'id' => $ids->get('VoucherB'),
'type' => LineItem::PROMOTION_LINE_ITEM_TYPE,
'code' => $ids->get('VoucherB'),
'identifier' => $ids->get('VoucherB'),
'payload' => ['promotionId' => $ids->get('voucherB')],
'quantity' => 1,
'label' => 'label',
'price' => new CalculatedPrice(200, 200, new CalculatedTaxCollection(), new TaxRuleCollection()),
'priceDefinition' => new QuantityPriceDefinition(200, new TaxRuleCollection(), 2),
],
],
'deliveries' => [],
'context' => '{}',
'payload' => '{}',
]],
Context::createDefaultContext()
);

return $id;
}
}

0 comments on commit b5c3ce3

Please sign in to comment.