diff --git a/src/ApiPlatform/Resources/Product/ProductSuppliers.php b/src/ApiPlatform/Resources/Product/ProductSuppliers.php new file mode 100644 index 000000000..df7058afd --- /dev/null +++ b/src/ApiPlatform/Resources/Product/ProductSuppliers.php @@ -0,0 +1,82 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +declare(strict_types=1); + +namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\Product; + +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductNotFoundException; +use PrestaShop\PrestaShop\Core\Domain\Product\Supplier\Command\RemoveAllAssociatedProductSuppliersCommand; +use PrestaShop\PrestaShop\Core\Domain\Product\Supplier\Command\SetProductDefaultSupplierCommand; +use PrestaShop\PrestaShop\Core\Domain\Product\Supplier\Command\SetSuppliersCommand; +use PrestaShop\PrestaShop\Core\Domain\Product\Supplier\Exception\ProductSupplierException; +use PrestaShopBundle\ApiPlatform\Metadata\CQRSDelete; +use PrestaShopBundle\ApiPlatform\Metadata\CQRSUpdate; +use Symfony\Component\HttpFoundation\Response; + +#[ApiResource( + operations: [ + new CQRSUpdate( + uriTemplate: '/products/{productId}/suppliers', + requirements: ['productId' => '\d+'], + output: false, + CQRSCommand: SetSuppliersCommand::class, + scopes: ['product_write'], + ), + new CQRSDelete( + uriTemplate: '/products/{productId}/suppliers', + requirements: ['productId' => '\d+'], + output: false, + CQRSCommand: RemoveAllAssociatedProductSuppliersCommand::class, + scopes: ['product_write'], + ), + new CQRSUpdate( + uriTemplate: '/products/{productId}/default-suppliers', + requirements: ['productId' => '\d+'], + output: false, + CQRSCommand: SetProductDefaultSupplierCommand::class, + scopes: ['product_write'], + ), + ], + exceptionToStatus: [ + ProductNotFoundException::class => Response::HTTP_NOT_FOUND, + ProductSupplierException::class => Response::HTTP_UNPROCESSABLE_ENTITY, + ], +)] +class ProductSuppliers +{ + #[ApiProperty(identifier: true)] + public int $productId; + + /** + * Supplier ids to associate with the product (used by the suppliers endpoint). + * + * @var int[] + */ + #[ApiProperty(openapiContext: ['type' => 'array', 'items' => ['type' => 'integer'], 'example' => [1]])] + public array $supplierIds; + + /** + * Supplier id to set as the product default (used by the default-suppliers endpoint). + */ + public int $defaultSupplierId; +} diff --git a/tests/Integration/ApiPlatform/ProductSuppliersEndpointTest.php b/tests/Integration/ApiPlatform/ProductSuppliersEndpointTest.php new file mode 100644 index 000000000..cc5b8ec86 --- /dev/null +++ b/tests/Integration/ApiPlatform/ProductSuppliersEndpointTest.php @@ -0,0 +1,109 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +declare(strict_types=1); + +namespace PsApiResourcesTest\Integration\ApiPlatform; + +use Symfony\Component\HttpFoundation\Response; +use Tests\Resources\DatabaseDump; + +class ProductSuppliersEndpointTest extends ApiTestCase +{ + private static int $productId; + private static int $supplierId; + + public static function setUpBeforeClass(): void + { + parent::setUpBeforeClass(); + self::createApiClient(['product_write']); + + self::$productId = (int) \Db::getInstance()->getValue( + 'SELECT `id_product` FROM `' . _DB_PREFIX_ . 'product` WHERE `product_type` = "standard" ORDER BY `id_product` ASC' + ); + self::$supplierId = (int) \Db::getInstance()->getValue( + 'SELECT `id_supplier` FROM `' . _DB_PREFIX_ . 'supplier` ORDER BY `id_supplier` ASC' + ); + } + + public static function tearDownAfterClass(): void + { + parent::tearDownAfterClass(); + DatabaseDump::restoreTables(['product_supplier', 'product']); + } + + public static function getProtectedEndpoints(): iterable + { + yield 'set suppliers endpoint' => ['PUT', '/products/1/suppliers']; + yield 'remove all suppliers endpoint' => ['DELETE', '/products/1/suppliers']; + yield 'set default supplier endpoint' => ['PUT', '/products/1/default-suppliers']; + } + + public function testSetProductSuppliers(): int + { + $this->updateItem( + '/products/' . self::$productId . '/suppliers', + ['supplierIds' => [self::$supplierId]], + ['product_write'], + Response::HTTP_NO_CONTENT + ); + + $count = (int) \Db::getInstance()->getValue( + 'SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'product_supplier` + WHERE `id_product` = ' . self::$productId . ' AND `id_supplier` = ' . self::$supplierId + ); + $this->assertGreaterThanOrEqual(1, $count); + + return self::$productId; + } + + /** + * @depends testSetProductSuppliers + */ + public function testSetProductDefaultSupplier(int $productId): int + { + $this->updateItem( + '/products/' . $productId . '/default-suppliers', + ['defaultSupplierId' => self::$supplierId], + ['product_write'], + Response::HTTP_NO_CONTENT + ); + + $defaultSupplier = (int) \Db::getInstance()->getValue( + 'SELECT `id_supplier` FROM `' . _DB_PREFIX_ . 'product` WHERE `id_product` = ' . $productId + ); + $this->assertSame(self::$supplierId, $defaultSupplier); + + return $productId; + } + + /** + * @depends testSetProductDefaultSupplier + */ + public function testRemoveAllProductSuppliers(int $productId): void + { + $this->deleteItem('/products/' . $productId . '/suppliers', ['product_write']); + + $count = (int) \Db::getInstance()->getValue( + 'SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'product_supplier` WHERE `id_product` = ' . $productId + ); + $this->assertSame(0, $count); + } +}