mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 17:10:53 +00:00
[ApiBundle] Add promotion coupon endpoint as a subresource
This commit is contained in:
parent
e6f4b2b344
commit
c0ac149d0e
11 changed files with 104 additions and 26 deletions
|
|
@ -19,4 +19,4 @@ Feature: Adding a new coupon
|
|||
And I make it valid until "21.04.2017"
|
||||
And I add it
|
||||
Then I should be notified that it has been successfully created
|
||||
And there should be a coupon with code "SANTA2016"
|
||||
And there should be a "Christmas sale" promotion with a coupon code "SANTA2016"
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ Feature: Browsing promotion coupons
|
|||
Scenario: Browsing coupons of a promotion
|
||||
When I want to view all coupons of this promotion
|
||||
And there should be 1 coupon related to this promotion
|
||||
And there should be a coupon with code "SANTA2016"
|
||||
And there should be a "Christmas sale" promotion with a coupon code "SANTA2016"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ Feature: Coupon validation
|
|||
|
||||
@api @no-ui
|
||||
Scenario: Trying to add a new coupon with no promotion
|
||||
Given I want to create a new coupon
|
||||
When I want to create a new coupon
|
||||
And I specify its code as "RANDOM"
|
||||
And I limit its usage to 30 times
|
||||
And I limit its per customer usage to 3 times
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ final class ManagingPromotionCouponsContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @When /^I want to modify the ("[^"]+" coupon) for this promotion$/
|
||||
* @When I want to modify the :coupon coupon for this promotion
|
||||
*/
|
||||
public function iWantToModifyTheCouponOfThisPromotion(PromotionCouponInterface $coupon): void
|
||||
{
|
||||
|
|
@ -74,8 +74,7 @@ final class ManagingPromotionCouponsContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @When /^I delete ("[^"]+" coupon) related to this promotion$/
|
||||
* @When /^I try to delete ("[^"]+" coupon) related to this promotion$/
|
||||
* @When I (try to) delete :coupon coupon related to this promotion
|
||||
*/
|
||||
public function iDeleteCouponRelatedToThisPromotion(PromotionCouponInterface $coupon): void
|
||||
{
|
||||
|
|
@ -186,22 +185,19 @@ final class ManagingPromotionCouponsContext implements Context
|
|||
*/
|
||||
public function thereShouldBeCountCouponsRelatedToThisPromotion(int $count, PromotionInterface $promotion): void
|
||||
{
|
||||
$coupons = $this->responseChecker->getCollectionItemsWithValue(
|
||||
$this->client->index(Resources::PROMOTION_COUPONS),
|
||||
'promotion',
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($promotion, 'admin'),
|
||||
$coupons = $this->responseChecker->getCollection(
|
||||
$this->client->subResourceIndex(Resources::PROMOTIONS, Resources::PROMOTION_COUPONS, $promotion->getCode()),
|
||||
);
|
||||
|
||||
Assert::same(count($coupons), $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then there should be a coupon with code :code
|
||||
* @Then there should be a :promotion promotion with a coupon code :code
|
||||
*/
|
||||
public function thereShouldBeACouponWithCode(string $code): void
|
||||
public function thereShouldBeACouponWithCode(PromotionInterface $promotion, string $code): void
|
||||
{
|
||||
Assert::true($this->responseChecker->hasItemWithValue(
|
||||
$this->client->index(Resources::PROMOTION_COUPONS),
|
||||
$this->client->subResourceIndex(Resources::PROMOTIONS, Resources::PROMOTION_COUPONS, $promotion->getCode()),
|
||||
'code',
|
||||
$code,
|
||||
));
|
||||
|
|
@ -340,10 +336,13 @@ final class ManagingPromotionCouponsContext implements Context
|
|||
*/
|
||||
public function thereShouldStillBeOnlyOneCouponWithCodeRelatedTo(string $code, PromotionInterface $promotion): void
|
||||
{
|
||||
Assert::count(
|
||||
$this->responseChecker->getCollectionItemsWithValue($this->client->index(Resources::PROMOTION_COUPONS), 'code', $code),
|
||||
1
|
||||
$coupons = $this->responseChecker->getCollectionItemsWithValue(
|
||||
$this->client->subResourceIndex(Resources::PROMOTIONS, Resources::PROMOTION_COUPONS, $promotion->getCode()),
|
||||
'code',
|
||||
$code
|
||||
);
|
||||
|
||||
Assert::count($coupons, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -322,8 +322,9 @@ final class ManagingPromotionCouponsContext implements Context
|
|||
|
||||
/**
|
||||
* @Then there should be a coupon with code :code
|
||||
* @Then there should be a :promotion promotion with a coupon code :code
|
||||
*/
|
||||
public function thereShouldBeCouponWithCode($code)
|
||||
public function thereShouldBeCouponWithCode(string $code): void
|
||||
{
|
||||
Assert::true($this->indexPage->isSingleResourceOnPage(['code' => $code]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,13 @@ Example configuration for `order_fixed_discount` action type:
|
|||
</itemOperation>
|
||||
</itemOperations>
|
||||
|
||||
<subresourceOperations>
|
||||
<subresourceOperation name="coupons_get_subresource">
|
||||
<attribute name="method">GET</attribute>
|
||||
<attribute name="path">/admin/promotions/{code}/promotion-coupons</attribute>
|
||||
</subresourceOperation>
|
||||
</subresourceOperations>
|
||||
|
||||
<property name="id" identifier="false" writable="false" />
|
||||
<property name="code" identifier="true" readable="true" />
|
||||
<property name="name" readable="true" />
|
||||
|
|
@ -144,7 +151,9 @@ Example configuration for `order_fixed_discount` action type:
|
|||
<property name="startsAt" readable="true" />
|
||||
<property name="endsAt" readable="true" />
|
||||
<property name="couponBased" readable="true" />
|
||||
<property name="coupons" readable="true" />
|
||||
<property name="coupons" readable="true" writable="true">
|
||||
<subresource resourceClass="%sylius.model.promotion_coupon.class%" collection="true" />
|
||||
</property>
|
||||
<property name="rules" readable="true" />
|
||||
<property name="actions" readable="true" />
|
||||
<property name="channels" readable="true" />
|
||||
|
|
|
|||
|
|
@ -68,6 +68,14 @@
|
|||
</itemOperation>
|
||||
</itemOperations>
|
||||
|
||||
<subresourceOperations>
|
||||
<subresourceOperation name="api_promotions_coupons_get_subresource">
|
||||
<attribute name="normalization_context">
|
||||
<attribute name="groups">admin:promotion_coupon:read</attribute>
|
||||
</attribute>
|
||||
</subresourceOperation>
|
||||
</subresourceOperations>
|
||||
|
||||
<property name="id" identifier="false" writable="false" />
|
||||
<property name="code" identifier="true" readable="true" />
|
||||
<property name="usageLimit" readable="true" />
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ use Sylius\Bundle\PromotionBundle\Validator\Constraints\PromotionNotCouponBased;
|
|||
use Sylius\Component\Promotion\Model\PromotionCouponInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedValueException;
|
||||
|
||||
final class PromotionNotCouponBasedValidator extends ConstraintValidator
|
||||
{
|
||||
|
|
@ -27,11 +28,13 @@ final class PromotionNotCouponBasedValidator extends ConstraintValidator
|
|||
return;
|
||||
}
|
||||
|
||||
/** @var PromotionNotCouponBased $constraint */
|
||||
Assert::isInstanceOf($constraint, PromotionNotCouponBased::class);
|
||||
if (!$constraint instanceof PromotionNotCouponBased) {
|
||||
throw new UnexpectedTypeException($constraint, PromotionNotCouponBased::class);
|
||||
}
|
||||
|
||||
/** @var PromotionCouponInterface $value */
|
||||
Assert::isInstanceOf($value, PromotionCouponInterface::class);
|
||||
if (!$value instanceof PromotionCouponInterface) {
|
||||
throw new UnexpectedValueException($value, PromotionCouponInterface::class);
|
||||
}
|
||||
|
||||
$promotion = $value->getPromotion();
|
||||
if (null === $promotion) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ use Sylius\Component\Promotion\Model\PromotionInterface;
|
|||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
|
||||
|
||||
final class PromotionNotCouponBasedValidatorSpec extends ObjectBehavior
|
||||
|
|
@ -50,7 +52,7 @@ final class PromotionNotCouponBasedValidatorSpec extends ObjectBehavior
|
|||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this
|
||||
->shouldThrow(\InvalidArgumentException::class)
|
||||
->shouldThrow(UnexpectedTypeException::class)
|
||||
->during('validate', [$coupon, $constraint])
|
||||
;
|
||||
}
|
||||
|
|
@ -60,7 +62,7 @@ final class PromotionNotCouponBasedValidatorSpec extends ObjectBehavior
|
|||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this
|
||||
->shouldThrow(\InvalidArgumentException::class)
|
||||
->shouldThrow(UnexpectedValueException::class)
|
||||
->during('validate', [new \stdClass(), new PromotionNotCouponBased()])
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,28 @@ final class PromotionsTest extends JsonApiTestCase
|
|||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_gets_promotion_coupons(): void
|
||||
{
|
||||
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
/** @var PromotionInterface $promotion */
|
||||
$promotion = $fixtures['promotion_1_off'];
|
||||
|
||||
$this->client->request(
|
||||
method: 'GET',
|
||||
uri: sprintf('/api/v2/admin/promotions/%s/promotion-coupons', $promotion->getCode()),
|
||||
server: $header
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/promotion/get_promotion_coupons_response',
|
||||
Response::HTTP_OK,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_creates_promotion(): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"@context": "\/api\/v2\/contexts\/PromotionCoupon",
|
||||
"@id": "\/api\/v2\/admin\/promotions/dollar_off/promotion-coupons",
|
||||
"@type": "hydra:Collection",
|
||||
"hydra:member": [
|
||||
{
|
||||
"@id": "\/api\/v2\/admin\/promotion-coupons\/XYZ1",
|
||||
"@type": "PromotionCoupon",
|
||||
"perCustomerUsageLimit": 1,
|
||||
"reusableFromCancelledOrders": true,
|
||||
"code": "XYZ1",
|
||||
"usageLimit": 2,
|
||||
"used": 1,
|
||||
"promotion": "\/api\/v2\/admin\/promotions\/dollar_off",
|
||||
"expiresAt": null,
|
||||
"createdAt": @date@,
|
||||
"updatedAt": @date@
|
||||
},
|
||||
{
|
||||
"@id": "\/api\/v2\/admin\/promotion-coupons\/XYZ2",
|
||||
"@type": "PromotionCoupon",
|
||||
"perCustomerUsageLimit": null,
|
||||
"reusableFromCancelledOrders": false,
|
||||
"code": "XYZ2",
|
||||
"usageLimit": null,
|
||||
"used": 0,
|
||||
"promotion": "\/api\/v2\/admin\/promotions\/dollar_off",
|
||||
"expiresAt": @date@,
|
||||
"createdAt": @date@,
|
||||
"updatedAt": @date@
|
||||
}
|
||||
],
|
||||
"hydra:totalItems": 2
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue