[API] Upgrade Product Association resource to API Platform 3 (#16749)

| Q               | A
|-----------------|-----
| Branch?         | api-platform-3
| Bug fix?        | no
| New feature?    | no
| BC breaks?      | no
| Deprecations?   | no
| Related tickets | 
| License         | MIT

TODO:

- [ ] adjust Behat scenarios

<!--
 - Bug fixes must be submitted against the 1.13 branch
 - Features and deprecations must be submitted against the 1.14 branch
- Features, removing deprecations and BC breaks must be submitted
against the 2.0 branch
 - Make sure that the correct base branch is set

To be sure you are not breaking any Backward Compatibilities, check the
documentation:

https://docs.sylius.com/en/latest/book/organization/backward-compatibility-promise.html
-->
This commit is contained in:
Kamil Grygierzec 2024-08-21 11:31:57 +02:00 committed by GitHub
commit 299b0f2eb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 163 additions and 342 deletions

View file

@ -9,7 +9,7 @@ Feature: Adding associations to an existing product
And the store has "LG G3", "LG headphones" and "LG earphones" products
And I am logged in as an administrator
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Adding an association to an existing product
When I want to modify the "LG G3" product
And I associate as "Accessories" the "LG headphones" and "LG earphones" products

View file

@ -10,7 +10,7 @@ Feature: Adding a new product with associations
And the store has "LG headphones", "LG earphones", "LG G4" and "LG G5" products
And I am logged in as an administrator
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Adding a new product with associations
When I want to create a new simple product
And I specify its code as "lg_g3"
@ -25,7 +25,7 @@ Feature: Adding a new product with associations
And this product should also have an association "Alternatives" with products "LG G4" and "LG G5"
And the product "LG G3" should appear in the store
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Adding a new product with associations after changing associated items
When I want to create a new simple product
And I specify its code as "lg_g3"
@ -40,7 +40,7 @@ Feature: Adding a new product with associations
And this product should not have an association "Accessories" with product "LG earphones"
And the product "LG G3" should appear in the store
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Adding a new product with association with numeric code
Given the store has 123 product association type
When I want to create a new simple product

View file

@ -9,7 +9,7 @@ Feature: Changing associations of an existing product
And the store has "LG G3", "LG headphones" and "LG earphones" products
And I am logged in as an administrator
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Changing associated products of a product association
Given the product "LG G3" has an association "Accessories" with product "LG headphones"
When I want to modify the "LG G3" product
@ -30,7 +30,7 @@ Feature: Changing associations of an existing product
When I change this product association's product to the "LG earphones" product
Then this association should only have product "LG earphones"
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Removing an associated product of a product association
Given the product "LG G3" has an association "Accessories" with products "LG headphones" and "LG earphones"
When I want to modify the "LG G3" product

View file

@ -10,7 +10,7 @@ Feature: Preventing a potential XSS attack while selecting similar product
And the store has "<script>alert('xss')</script>" and "LG headphones" products
And I am logged in as an administrator
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Preventing a potential XSS attack while editing product
When I want to create a new simple product
Then I should be able to associate as "Accessories" the "LG headphones" product

View file

@ -70,7 +70,7 @@ Feature: Viewing details of a product with variants
And I should see product's meta keywords is "shield"
And I should see product's short description is "good shield"
@todo-api @ui
@api @ui
Scenario: Viewing association types
Given the store has a "Glass shield" product
And the product "Iron Shield" has an association "Similar" with product "Glass shield"

View file

@ -17,28 +17,28 @@ Feature: Viewing product's associations
And the product "LG G3" has also an association "Alternatives" with products "LG G4" and "LG G5"
And the product "LG Gram" has an association "Alternatives" with products "LG AC Adapter" and "LG headphones"
@ui @api
@api @ui
Scenario: Viewing a detailed page with product's associations in default locale
Given I am browsing channel "Smartphone Store"
When I view product "LG G3"
Then I should see the product association "Accessories" with products "LG headphones" and "LG earphones"
And I should also see the product association "Alternatives" with products "LG G4" and "LG G5"
@ui @api
@api @ui
Scenario: Viewing a detailed page with product's associations after locale change
Given I am browsing channel "Smartphone Store"
When I view product "LG G3" in the "Polish (Poland)" locale
Then I should see the product association "Akcesoria" with products "LG headphones" and "LG earphones"
And I should also see the product association "Alternatywy" with products "LG G4" and "LG G5"
@ui @api
@api @ui
Scenario: Viewing a detailed page with product's associations within current channel
Given I am browsing channel "Notebook Store"
When I view product "LG Gram"
Then I should see the product association "Alternatives" with product "LG AC Adapter"
And I should not see the product association "Alternatives" with product "LG headphones"
@ui @api
@api @ui
Scenario: Viewing a detailed page with enabled associated products only
Given the "LG G4" product is disabled
And I am browsing channel "Smartphone Store"
@ -47,7 +47,7 @@ Feature: Viewing product's associations
And I should also see the product association "Alternatives" with product "LG G5"
And I should not see the product association "Alternatives" with product "LG G4"
@ui @api
@api @ui
Scenario: Viewing a detailed page while an empty association exists
Given products "LG G4" and "LG G5" are disabled
And I am browsing channel "Smartphone Store"

View file

@ -17,15 +17,15 @@ use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\QueryBuilder;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Bundle\ApiBundle\SectionResolver\ShopApiSection;
use Sylius\Bundle\ApiBundle\Serializer\ContextKeys;
use Sylius\Component\Core\Model\AdminUserInterface;
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
use Sylius\Component\Product\Model\ProductAssociationInterface;
use Webmozart\Assert\Assert;
final readonly class EnabledProductsExtension implements QueryItemExtensionInterface
{
public function __construct(private UserContextInterface $userContext)
public function __construct(private SectionProviderInterface $sectionProvider)
{
}
@ -45,8 +45,7 @@ final readonly class EnabledProductsExtension implements QueryItemExtensionInter
return;
}
$user = $this->userContext->getUser();
if ($user instanceof AdminUserInterface && in_array('ROLE_API_ACCESS', $user->getRoles(), true)) {
if (!$this->sectionProvider->getSection() instanceof ShopApiSection) {
return;
}

View file

@ -15,8 +15,5 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/properties-3.0 https://api-platform.com/schema/metadata/properties-3.0.xsd"
>
<property resource="Sylius\Component\Product\Model\ProductAssociation" name="id" identifier="true" writable="false"/>
<property resource="Sylius\Component\Product\Model\ProductAssociation" name="type" required="true"/>
<property resource="Sylius\Component\Product\Model\ProductAssociation" name="owner" required="true"/>
<property resource="Sylius\Component\Product\Model\ProductAssociation" name="associatedProducts" required="true"/>
<property resource="Sylius\Component\Product\Model\ProductAssociation" name="id" identifier="true" />
</properties>

View file

@ -17,17 +17,8 @@
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0 https://api-platform.com/schema/metadata/resources-3.0.xsd"
>
<resource class="Sylius\Component\Product\Model\ProductAssociation">
<validationContext>
<values>
<value name="groups">
<values>
<value>sylius</value>
</values>
</value>
</values>
</validationContext>
<operations>
<!-- Admin operations-->
<operation class="ApiPlatform\Metadata\GetCollection" uriTemplate="/admin/product-associations">
<normalizationContext>
<values>
@ -39,10 +30,22 @@
</values>
</normalizationContext>
<filters>
<filter>sylius.api.product_association_filter</filter>
<filter>sylius_api.search_filter.admin.product_association</filter>
</filters>
</operation>
<operation class="ApiPlatform\Metadata\Get" uriTemplate="/admin/product-associations/{id}">
<normalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:admin:product_association:show</value>
</values>
</value>
</values>
</normalizationContext>
</operation>
<operation class="ApiPlatform\Metadata\Post" uriTemplate="/admin/product-associations">
<denormalizationContext>
<values>
@ -62,18 +65,15 @@
</value>
</values>
</normalizationContext>
</operation>
<operation class="ApiPlatform\Metadata\Get" uriTemplate="/admin/product-associations/{id}">
<normalizationContext>
<validationContext>
<values>
<value name="groups">
<values>
<value>sylius:admin:product_association:show</value>
<value>sylius</value>
</values>
</value>
</values>
</normalizationContext>
</validationContext>
</operation>
<operation class="ApiPlatform\Metadata\Put" uriTemplate="/admin/product-associations/{id}">
@ -95,10 +95,20 @@
</value>
</values>
</normalizationContext>
<validationContext>
<values>
<value name="groups">
<values>
<value>sylius</value>
</values>
</value>
</values>
</validationContext>
</operation>
<operation class="ApiPlatform\Metadata\Delete" uriTemplate="/admin/product-associations/{id}" />
<!-- Shop operations-->
<operation class="ApiPlatform\Metadata\Get" uriTemplate="/shop/product-associations/{id}">
<normalizationContext>
<values>
@ -110,8 +120,6 @@
</values>
</normalizationContext>
</operation>
</operations>
</resource>
</resources>

View file

@ -1,105 +0,0 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Sylius Sp. z o.o.
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata https://api-platform.com/schema/metadata/metadata-2.0.xsd"
>
<resource class="%sylius.model.product_association.class%" shortName="ProductAssociation">
<attribute name="validation_groups">sylius</attribute>
<collectionOperations>
<collectionOperation name="admin_get">
<attribute name="method">GET</attribute>
<attribute name="path">/admin/product-associations</attribute>
<attribute name="filters">
<attribute>sylius.api.product_association_filter</attribute>
</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:product_association:index</attribute>
<attribute>sylius:admin:product_association:index</attribute>
</attribute>
</attribute>
</collectionOperation>
<collectionOperation name="admin_post">
<attribute name="method">POST</attribute>
<attribute name="path">/admin/product-associations</attribute>
<attribute name="denormalization_context">
<attribute name="groups">
<attribute>admin:product_association:create</attribute>
<attribute>sylius:admin:product_association:create</attribute>
</attribute>
</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:product_association:show</attribute>
<attribute>sylius:admin:product_association:show</attribute>
</attribute>
</attribute>
</collectionOperation>
</collectionOperations>
<itemOperations>
<itemOperation name="admin_get">
<attribute name="method">GET</attribute>
<attribute name="path">/admin/product-associations/{id}</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:product_association:show</attribute>
<attribute>sylius:admin:product_association:show</attribute>
</attribute>
</attribute>
</itemOperation>
<itemOperation name="admin_put">
<attribute name="method">PUT</attribute>
<attribute name="path">/admin/product-associations/{id}</attribute>
<attribute name="denormalization_context">
<attribute name="groups">
<attribute>admin:product_association:update</attribute>
<attribute>sylius:admin:product_association:update</attribute>
</attribute>
</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:product_association:show</attribute>
<attribute>sylius:admin:product_association:show</attribute>
</attribute>
</attribute>
</itemOperation>
<itemOperation name="admin_delete">
<attribute name="method">DELETE</attribute>
<attribute name="path">/admin/product-associations/{id}</attribute>
</itemOperation>
<itemOperation name="shop_get">
<attribute name="method">GET</attribute>
<attribute name="path">/shop/product-associations/{id}</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>shop:product_association:show</attribute>
<attribute>sylius:shop:product_association:show</attribute>
</attribute>
</attribute>
</itemOperation>
</itemOperations>
<property name="id" identifier="true" writable="false" />
<property name="type" identifier="false" required="true" />
<property name="owner" required="true" />
<property name="associatedProducts" required="true" />
</resource>
</resources>

View file

@ -17,37 +17,27 @@
>
<class name="Sylius\Component\Product\Model\ProductAssociation">
<attribute name="id">
<group>shop:product_association:show</group>
<group>sylius:shop:product_association:show</group>
</attribute>
<attribute name="type">
<group>admin:product_association:index</group>
<group>sylius:admin:product_association:index</group>
<group>admin:product_association:show</group>
<group>sylius:admin:product_association:show</group>
<group>admin:product_association:create</group>
<group>sylius:admin:product_association:create</group>
<group>shop:product_association:show</group>
<group>sylius:shop:product_association:show</group>
</attribute>
<attribute name="owner">
<group>admin:product_association:index</group>
<group>sylius:admin:product_association:index</group>
<group>admin:product_association:show</group>
<group>sylius:admin:product_association:show</group>
<group>admin:product_association:create</group>
<group>sylius:admin:product_association:create</group>
</attribute>
<attribute name="associatedProducts">
<group>admin:product_association:index</group>
<group>sylius:admin:product_association:index</group>
<group>admin:product_association:show</group>
<group>sylius:admin:product_association:show</group>
<group>admin:product_association:create</group>
<group>sylius:admin:product_association:create</group>
<group>admin:product_association:update</group>
<group>sylius:admin:product_association:update</group>
<group>shop:product_association:show</group>
<group>sylius:shop:product_association:show</group>
</attribute>
</class>

View file

@ -147,7 +147,7 @@
</service>
<service id="sylius_api.doctrine.orm.query_extension.shop.product_association.enabled_products" class="Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\ProductAssociation\EnabledProductsExtension">
<argument type="service" id="Sylius\Bundle\ApiBundle\Context\UserContextInterface" />
<argument type="service" id="sylius.section_resolver.uri_based_section_resolver" />
<tag name="api_platform.doctrine.orm.query_extension.item" />
</service>

View file

@ -88,7 +88,7 @@
<tag name="api_platform.filter" />
</service>
<service id="sylius.api.product_association_filter" parent="api_platform.doctrine.orm.search_filter" public="true">
<service id="sylius_api.search_filter.admin.product_association" parent="api_platform.doctrine.orm.search_filter" public="true">
<argument type="collection">
<argument key="owner.code">partial</argument>
<argument key="type.code">partial</argument>

View file

@ -17,28 +17,28 @@ use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Get;
use Doctrine\ORM\QueryBuilder;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Bundle\ApiBundle\SectionResolver\AdminApiSection;
use Sylius\Bundle\ApiBundle\SectionResolver\ShopApiSection;
use Sylius\Bundle\ApiBundle\Serializer\ContextKeys;
use Sylius\Component\Core\Model\AdminUserInterface;
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Sylius\Component\Product\Model\ProductAssociationInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserInterface;
final class EnabledProductsExtensionSpec extends ObjectBehavior
{
function let(UserContextInterface $userContext): void
function let(SectionProviderInterface $sectionProvider): void
{
$this->beConstructedWith($userContext);
$this->beConstructedWith($sectionProvider);
}
function it_does_nothing_if_current_resource_is_not_a_product_association(
UserContextInterface $userContext,
SectionProviderInterface $sectionProvider,
QueryBuilder $queryBuilder,
QueryNameGeneratorInterface $queryNameGenerator,
): void {
$userContext->getUser()->shouldNotBeCalled();
$sectionProvider->getSection()->shouldNotBeCalled();
$queryBuilder->getRootAliases()->shouldNotBeCalled();
$this->applyToItem(
@ -50,16 +50,13 @@ final class EnabledProductsExtensionSpec extends ObjectBehavior
);
}
function it_does_nothing_if_current_user_is_an_admin_user(
UserContextInterface $userContext,
AdminUserInterface $adminUser,
function it_does_nothing_if_section_is_not_shop_api(
SectionProviderInterface $sectionProvider,
QueryBuilder $queryBuilder,
QueryNameGeneratorInterface $queryNameGenerator,
AdminApiSection $section,
): void {
$userContext->getUser()->willReturn($adminUser);
$adminUser->getRoles()->willReturn(['ROLE_API_ACCESS']);
$queryBuilder->getRootAliases()->shouldNotBeCalled();
$sectionProvider->getSection()->willReturn($section);
$this->applyToItem(
$queryBuilder,
@ -71,14 +68,13 @@ final class EnabledProductsExtensionSpec extends ObjectBehavior
}
function it_applies_conditions_for_customer(
UserContextInterface $userContext,
UserInterface $user,
SectionProviderInterface $sectionProvider,
QueryBuilder $queryBuilder,
QueryNameGeneratorInterface $queryNameGenerator,
ChannelInterface $channel,
ShopApiSection $section,
): void {
$userContext->getUser()->willReturn($user);
$user->getRoles()->willReturn([]);
$sectionProvider->getSection()->willReturn($section);
$queryNameGenerator->generateParameterName('enabled')->shouldBeCalled()->willReturn('enabled');
$queryNameGenerator->generateParameterName('channel')->shouldBeCalled()->willReturn('channel');

View file

@ -19,8 +19,34 @@ use Symfony\Component\HttpFoundation\Response;
final class ProductAssociationsTest extends JsonApiTestCase
{
protected function setUp(): void
{
$this->setUpAdminContext();
$this->setUpDefaultGetHeaders();
$this->setUpDefaultPostHeaders();
$this->setUpDefaultPutHeaders();
parent::setUp();
}
/** @test */
public function it_gets_product_association(): void
public function it_gets_product_associations(): void
{
$this->loadFixturesFromFiles([
'product/product_with_many_locales.yaml',
'authentication/api_administrator.yaml',
]);
$this->requestGet('/api/v2/admin/product-associations');
$this->assertResponse(
$this->client->getResponse(),
'admin/product_association/get_product_association_collection_response',
);
}
/** @test */
public function it_gets_a_product_association(): void
{
$fixtures = $this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
@ -29,20 +55,12 @@ final class ProductAssociationsTest extends JsonApiTestCase
/** @var ProductAssociationInterface $association */
$association = $fixtures['product_association'];
$this->client->request(
method: 'GET',
uri: sprintf('/api/v2/admin/product-associations/%s', $association->getId()),
server: $this
->headerBuilder()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build(),
);
$this->requestGet(sprintf('/api/v2/admin/product-associations/%s', $association->getId()));
$this->assertResponse(
$this->client->getResponse(),
'admin/product_association/get_product_association_response',
Response::HTTP_OK,
);
}
@ -54,70 +72,29 @@ final class ProductAssociationsTest extends JsonApiTestCase
'product/product_with_many_locales.yaml',
]);
$this->client->request(
method: 'GET',
uri: '/api/v2/admin/product-associations/nope',
server: $this
->headerBuilder()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build(),
);
$this->requestGet('/api/v2/admin/product-associations/wrong_id');
$response = $this->client->getResponse();
$this->assertSame(Response::HTTP_NOT_FOUND, $response->getStatusCode());
}
/** @test */
public function it_returns_product_association_collection(): void
public function it_creates_a_product_association(): void
{
$this->loadFixturesFromFiles([
'product/product_with_many_locales.yaml',
'authentication/api_administrator.yaml',
]);
$this->client->request(
method: 'GET',
uri: '/api/v2/admin/product-associations',
server: $this
->headerBuilder()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build(),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/product_association/get_product_association_collection_response',
Response::HTTP_OK,
);
}
/** @test */
public function it_creates_product_association(): void
{
$this->loadFixturesFromFiles([
'product/products_with_associations.yaml',
'authentication/api_administrator.yaml',
]);
$this->client->request(
method: 'POST',
uri: '/api/v2/admin/product-associations',
server: $this
->headerBuilder()
->withJsonLdContentType()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build(),
content: json_encode([
$this->requestPost(
'/api/v2/admin/product-associations',
[
'type' => '/api/v2/admin/product-association-types/similar_products',
'owner' => '/api/v2/admin/products/CUP',
'associatedProducts' => [
'/api/v2/admin/products/MUG',
],
], \JSON_THROW_ON_ERROR),
],
);
$this->assertResponse(
@ -127,94 +104,15 @@ final class ProductAssociationsTest extends JsonApiTestCase
);
}
/** @test */
public function it_updates_product_association(): void
{
$fixtures = $this->loadFixturesFromFiles([
'product/products_with_associations.yaml',
'authentication/api_administrator.yaml',
]);
/** @var ProductAssociationInterface $association */
$association = $fixtures['product_association'];
$this->client->request(
method: 'PUT',
uri: sprintf('/api/v2/admin/product-associations/%s', $association->getId()),
server: $this
->headerBuilder()
->withJsonLdContentType()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build(),
content: json_encode([
'associatedProducts' => [
'/api/v2/admin/products/TANKARD',
],
], \JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/product_association/put_product_association_response',
Response::HTTP_OK,
);
}
/** @test */
public function it_deletes_product_association(): void
{
$fixtures = $this->loadFixturesFromFiles([
'product/products_with_associations.yaml',
'authentication/api_administrator.yaml',
]);
$header = $this
->headerBuilder()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build()
;
/** @var ProductAssociationInterface $association */
$association = $fixtures['product_association'];
$associationId = $association->getId();
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/product-associations/%s', $associationId),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
$this->client->request(
method: 'GET',
uri: sprintf('/api/v2/admin/product-associations/%s', $associationId),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NOT_FOUND);
}
/** @test */
public function it_does_not_create_product_association_without_required_data(): void
{
$this->loadFixturesFromFiles([
'product/products_with_associations.yaml',
'authentication/api_administrator.yaml',
'product/products_with_associations.yaml',
]);
$this->client->request(
method: 'POST',
uri: '/api/v2/admin/product-associations',
server: $this
->headerBuilder()
->withJsonLdContentType()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build(),
content: '{}',
);
$this->requestPost('/api/v2/admin/product-associations', []);
$this->assertResponse(
$this->client->getResponse(),
@ -227,26 +125,19 @@ final class ProductAssociationsTest extends JsonApiTestCase
public function it_does_not_create_duplicated_product_association(): void
{
$fixtures = $this->loadFixturesFromFiles([
'product/products_with_associations.yaml',
'authentication/api_administrator.yaml',
'product/products_with_associations.yaml',
]);
/** @var ProductAssociationInterface $association */
$association = $fixtures['product_association'];
$this->client->request(
method: 'POST',
uri: '/api/v2/admin/product-associations',
server: $this
->headerBuilder()
->withJsonLdContentType()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build(),
content: json_encode([
$this->requestPost(
'/api/v2/admin/product-associations',
[
'type' => sprintf('/api/v2/admin/product-association-types/%s', $association->getType()->getCode()),
'owner' => sprintf('/api/v2/admin/products/%s', $association->getOwner()->getCode()),
], \JSON_THROW_ON_ERROR),
],
);
$this->assertResponse(
@ -255,4 +146,51 @@ final class ProductAssociationsTest extends JsonApiTestCase
Response::HTTP_UNPROCESSABLE_ENTITY,
);
}
/** @test */
public function it_updates_a_product_association(): void
{
$fixtures = $this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
'product/products_with_associations.yaml',
]);
/** @var ProductAssociationInterface $association */
$association = $fixtures['product_association'];
$this->requestPut(
sprintf('/api/v2/admin/product-associations/%s', $association->getId()),
[
'associatedProducts' => [
'/api/v2/admin/products/TANKARD',
],
],
);
$this->assertResponse(
$this->client->getResponse(),
'admin/product_association/put_product_association_response',
);
}
/** @test */
public function it_deletes_a_product_association(): void
{
$fixtures = $this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
'product/products_with_associations.yaml',
]);
/** @var ProductAssociationInterface $association */
$association = $fixtures['product_association'];
$associationId = $association->getId();
$this->requestDelete(sprintf('/api/v2/admin/product-associations/%s', $associationId));
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
$this->requestGet(sprintf('/api/v2/admin/product-associations/%s', $associationId));
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NOT_FOUND);
}
}

View file

@ -19,6 +19,13 @@ use Symfony\Component\HttpFoundation\Response;
final class ProductAssociationsTest extends JsonApiTestCase
{
protected function setUp(): void
{
$this->setUpDefaultGetHeaders();
parent::setUp();
}
/** @test */
public function it_gets_product_association(): void
{
@ -26,16 +33,12 @@ final class ProductAssociationsTest extends JsonApiTestCase
/** @var ProductAssociationInterface $association */
$association = $fixtures['product_association'];
$this->client->request(
method: 'GET',
uri: sprintf('/api/v2/shop/product-associations/%s', $association->getId()),
server: self::CONTENT_TYPE_HEADER,
);
$this->requestGet(sprintf('/api/v2/shop/product-associations/%s', $association->getId()));
$this->assertResponse(
$this->client->getResponse(),
'shop/product_association/get_product_association_response',
Response::HTTP_OK,
);
}
@ -44,14 +47,9 @@ final class ProductAssociationsTest extends JsonApiTestCase
{
$this->loadFixturesFromFile('product/product_with_many_locales.yaml');
$this->client->request(
method: 'GET',
uri: sprintf('/api/v2/shop/product-associations/%s', 'wrong input'),
server: self::CONTENT_TYPE_HEADER,
);
$this->requestGet(sprintf('/api/v2/shop/product-associations/%s', 'wrong_id'));
$response = $this->client->getResponse();
$this->assertSame(Response::HTTP_NOT_FOUND, $response->getStatusCode());
}
}