diff --git a/ci.sh b/ci.sh
new file mode 100755
index 0000000..ac6347f
--- /dev/null
+++ b/ci.sh
@@ -0,0 +1,24 @@
+BASEDIR=$(dirname "$(readlink -f "$0")")
+# create a tmp directory and clone the project & check if folder exist before running this cmd
+if [ ! -d "/tmp" ]; then
+ mkdir /tmp
+fi
+cd /tmp
+if [ ! -d "/tmp/prestashop" ]; then
+ git clone git@github.com:PrestaShop/PrestaShop.git prestashop
+ # Remove this next line once the file has been transfered from the core to the module.
+ rm prestashop/src/PrestaShopBundle/ApiPlatform/Resources/Hook.php
+fi
+cd /tmp/prestashop
+git checkout develop
+# install the project & mysql
+rm -rf /tmp/prestashop/modules/ps_apiresources/*
+cp -r $BASEDIR/* /tmp/prestashop/modules/ps_apiresources
+PS_INSTALL_AUTO=0 docker compose build --no-cache && docker compose up -d --force-recreate
+bash -c 'while [[ "$(curl -L -s -o /dev/null -w %{http_code} 'http://localhost:8001/install-dev/')" != "200" ]]; do echo "waiting for shop install"; sleep 5; done'
+# install the module
+docker-compose exec prestashop-git composer i --working-dir=/var/www/html/modules/ps_apiresources
+#docker-compose exec prestashop-git bin/console prestashop:module install ps_apiresources
+docker-compose exec prestashop-git composer create-test-db
+# run tests
+docker-compose exec prestashop-git vendor/bin/phpunit modules/ps_apiresources/tests/* -c modules/ps_apiresources/tests/Integration/phpunit.xml
\ No newline at end of file
diff --git a/composer.json b/composer.json
index d738a81..c2511d0 100644
--- a/composer.json
+++ b/composer.json
@@ -21,6 +21,11 @@
},
"classmap": ["ps_apiresources.php"]
},
+ "autoload-dev": {
+ "psr-4": {
+ "PsApiResourcesTest\\": "tests/"
+ }
+ },
"config": {
"preferred-install": "dist",
"classmap-authoritative": true,
diff --git a/src/ApiPlatform/Resources/Hook.php b/src/ApiPlatform/Resources/Hook.php
new file mode 100644
index 0000000..9f4519b
--- /dev/null
+++ b/src/ApiPlatform/Resources/Hook.php
@@ -0,0 +1,107 @@
+
+ * @copyright Since 2007 PrestaShop SA and Contributors
+ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ */
+
+declare(strict_types=1);
+
+namespace PrestaShop\Module\APIResources\ApiPlatform\Resources;
+
+use ApiPlatform\Core\Annotation\ApiProperty;
+use ApiPlatform\Metadata\ApiResource;
+use ApiPlatform\Metadata\Get;
+use ApiPlatform\Metadata\Put;
+use PrestaShop\PrestaShop\Core\Domain\Hook\Command\UpdateHookStatusCommand;
+use PrestaShop\PrestaShop\Core\Domain\Hook\Exception\HookNotFoundException;
+use PrestaShop\PrestaShop\Core\Domain\Hook\Query\GetHook;
+use PrestaShop\PrestaShop\Core\Domain\Hook\Query\GetHookStatus;
+use PrestaShopBundle\ApiPlatform\Processor\CommandProcessor;
+use PrestaShopBundle\ApiPlatform\Provider\QueryProvider;
+
+#[ApiResource(
+ operations: [
+ new Get(
+ uriTemplate: '/hook-status/{id}',
+ requirements: ['id' => '\d+'],
+ openapiContext: [
+ 'summary' => 'Get hook status A',
+ 'description' => 'Get hook status B',
+ 'parameters' => [
+ [
+ 'name' => 'id',
+ 'in' => 'path',
+ 'required' => true,
+ 'schema' => [
+ 'type' => 'string',
+ ],
+ 'description' => 'Id of the hook you are requesting the status from',
+ ],
+ [
+ 'name' => 'Authorization',
+ 'in' => 'scopes',
+ 'description' => 'hook_read
hook_write ',
+ ],
+ ],
+ ],
+ exceptionToStatus: [HookNotFoundException::class => 404],
+ provider: QueryProvider::class,
+ extraProperties: [
+ 'CQRSQuery' => GetHookStatus::class,
+ 'scopes' => ['hook_read'],
+ ]
+ ),
+ new Put(
+ uriTemplate: '/hook-status',
+ processor: CommandProcessor::class,
+ extraProperties: [
+ 'CQRSCommand' => UpdateHookStatusCommand::class,
+ 'scopes' => ['hook_write'],
+ ]
+ ),
+ new Get(
+ uriTemplate: '/hooks/{id}',
+ requirements: ['id' => '\d+'],
+ exceptionToStatus: [HookNotFoundException::class => 404],
+ provider: QueryProvider::class,
+ extraProperties: [
+ 'CQRSQuery' => GetHook::class,
+ 'scopes' => ['hook_read'],
+ ]
+ ),
+ ],
+)]
+class Hook
+{
+ #[ApiProperty(identifier: true)]
+ public int $id;
+
+ public bool $active;
+
+ public string $name;
+
+ public string $title;
+
+ public string $description;
+}
diff --git a/tests/Integration/ApiPlatform/ApiTestCase.php b/tests/Integration/ApiPlatform/ApiTestCase.php
new file mode 100644
index 0000000..b22cc90
--- /dev/null
+++ b/tests/Integration/ApiPlatform/ApiTestCase.php
@@ -0,0 +1,100 @@
+
+ * @copyright Since 2007 PrestaShop SA and Contributors
+ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ */
+
+declare(strict_types=1);
+
+namespace PsApiResourcesTest\Integration\ApiPlatform;
+
+use ApiPlatform\Symfony\Bundle\Test\Client;
+use PrestaShop\PrestaShop\Core\Domain\ApiAccess\Command\AddApiAccessCommand;
+use Tests\Resources\DatabaseDump;
+
+abstract class ApiTestCase extends \ApiPlatform\Symfony\Bundle\Test\ApiTestCase
+{
+ protected const CLIENT_ID = 'test_client_id';
+ protected const CLIENT_NAME = 'test_client_name';
+
+ protected static ?string $clientSecret = null;
+
+ public static function setUpBeforeClass(): void
+ {
+ parent::setUpBeforeClass();
+ DatabaseDump::restoreTables(['api_access']);
+ }
+
+ public static function tearDownAfterClass(): void
+ {
+ parent::tearDownAfterClass();
+ DatabaseDump::restoreTables(['api_access']);
+ self::$clientSecret = null;
+ }
+
+ protected static function createClient(array $kernelOptions = [], array $defaultOptions = []): Client
+ {
+ if (!isset($defaultOptions['headers']['accept'])) {
+ $defaultOptions['headers']['accept'] = ['application/json'];
+ }
+
+ return parent::createClient($kernelOptions, $defaultOptions);
+ }
+
+ protected function getBearerToken(array $scopes = []): string
+ {
+ if (null === self::$clientSecret) {
+ self::createApiAccess($scopes);
+ }
+ $client = static::createClient();
+ $parameters = ['parameters' => [
+ 'client_id' => static::CLIENT_ID,
+ 'client_secret' => static::$clientSecret,
+ 'grant_type' => 'client_credentials',
+ 'scope' => $scopes,
+ ]];
+ $options = ['extra' => $parameters];
+ $response = $client->request('POST', '/api/oauth2/token', $options);
+
+ return json_decode($response->getContent())->access_token;
+ }
+
+ protected static function createApiAccess(array $scopes = [], int $lifetime = 10000): void
+ {
+ $client = static::createClient();
+ $command = new AddApiAccessCommand(
+ static::CLIENT_NAME,
+ static::CLIENT_ID,
+ true,
+ '',
+ $lifetime,
+ $scopes
+ );
+
+ $container = $client->getContainer();
+ $commandBus = $container->get('prestashop.core.command_bus');
+ $createdApiAccess = $commandBus->handle($command);
+
+ self::$clientSecret = $createdApiAccess->getSecret();
+ }
+}
diff --git a/tests/Integration/ApiPlatform/GetHookStatusTest.php b/tests/Integration/ApiPlatform/GetHookStatusTest.php
new file mode 100644
index 0000000..3f53338
--- /dev/null
+++ b/tests/Integration/ApiPlatform/GetHookStatusTest.php
@@ -0,0 +1,124 @@
+
+ * @copyright Since 2007 PrestaShop SA and Contributors
+ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ */
+
+declare(strict_types=1);
+
+namespace PsApiResourcesTest\Integration\ApiPlatform;
+
+use Tests\Resources\DatabaseDump;
+
+class GetHookStatusTest extends ApiTestCase
+{
+ public static function setUpBeforeClass(): void
+ {
+ parent::setUpBeforeClass();
+ DatabaseDump::restoreTables(['hook']);
+ }
+
+ public static function tearDownAfterClass(): void
+ {
+ parent::tearDownAfterClass();
+ DatabaseDump::restoreTables(['hook']);
+ }
+
+ public function testGetHookStatus(): void
+ {
+ $inactiveHook = new \Hook();
+ $inactiveHook->name = 'inactiveHook';
+ $inactiveHook->active = false;
+ $inactiveHook->add();
+
+ $activeHook = new \Hook();
+ $activeHook->name = 'activeHook';
+ $activeHook->active = true;
+ $activeHook->add();
+
+ $bearerToken = $this->getBearerToken([
+ 'hook_read',
+ 'hook_write',
+ ]);
+ $response = static::createClient()->request('GET', '/api/hook-status/' . (int) $inactiveHook->id, ['auth_bearer' => $bearerToken]);
+ self::assertEquals(json_decode($response->getContent())->active, $inactiveHook->active);
+ self::assertResponseStatusCodeSame(200);
+
+ $response = static::createClient()->request('GET', '/api/hook-status/' . (int) $activeHook->id, ['auth_bearer' => $bearerToken]);
+ self::assertEquals(json_decode($response->getContent())->active, $activeHook->active);
+ self::assertResponseStatusCodeSame(200);
+
+ static::createClient()->request('GET', '/api/hook-status/' . 9999, ['auth_bearer' => $bearerToken]);
+ self::assertResponseStatusCodeSame(404);
+
+ static::createClient()->request('GET', '/api/hook-status/' . $activeHook->id);
+ self::assertResponseStatusCodeSame(401);
+
+ $inactiveHook->delete();
+ $activeHook->delete();
+ }
+
+ public function testDisableHook(): void
+ {
+ $hook = new \Hook();
+ $hook->name = 'disableHook';
+ $hook->active = true;
+ $hook->add();
+
+ $bearerToken = $this->getBearerToken([
+ 'hook_read',
+ 'hook_write',
+ ]);
+ static::createClient()->request('PUT', '/api/hook-status', [
+ 'auth_bearer' => $bearerToken,
+ 'json' => ['id' => (int) $hook->id, 'active' => false],
+ ]);
+ self::assertResponseStatusCodeSame(200);
+
+ $response = static::createClient()->request('GET', '/api/hook-status/' . (int) $hook->id, ['auth_bearer' => $bearerToken]);
+ self::assertEquals(json_decode($response->getContent())->active, false);
+ self::assertResponseStatusCodeSame(200);
+ }
+
+ public function testEnableHook(): void
+ {
+ $hook = new \Hook();
+ $hook->name = 'enableHook';
+ $hook->active = false;
+ $hook->add();
+
+ $bearerToken = $this->getBearerToken([
+ 'hook_read',
+ 'hook_write',
+ ]);
+ static::createClient()->request('PUT', '/api/hook-status', [
+ 'auth_bearer' => $bearerToken,
+ 'json' => ['id' => (int) $hook->id, 'active' => true],
+ ]);
+ self::assertResponseStatusCodeSame(200);
+
+ $response = static::createClient()->request('GET', '/api/hook-status/' . (int) $hook->id, ['auth_bearer' => $bearerToken]);
+ self::assertEquals(json_decode($response->getContent())->active, true);
+ self::assertResponseStatusCodeSame(200);
+ }
+}
diff --git a/tests/Integration/ApiPlatform/GetHookTest.php b/tests/Integration/ApiPlatform/GetHookTest.php
new file mode 100644
index 0000000..550302b
--- /dev/null
+++ b/tests/Integration/ApiPlatform/GetHookTest.php
@@ -0,0 +1,72 @@
+
+ * @copyright Since 2007 PrestaShop SA and Contributors
+ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ */
+
+declare(strict_types=1);
+
+namespace PsApiResourcesTest\Integration\ApiPlatform;
+
+use Tests\Resources\DatabaseDump;
+
+class GetHookTest extends ApiTestCase
+{
+ public static function setUpBeforeClass(): void
+ {
+ parent::setUpBeforeClass();
+ DatabaseDump::restoreTables(['hook']);
+ }
+
+ public static function tearDownAfterClass(): void
+ {
+ parent::tearDownAfterClass();
+ DatabaseDump::restoreTables(['hook']);
+ }
+
+ public function testGetHook(): void
+ {
+ $hook = new \Hook();
+ $hook->name = 'testHook';
+ $hook->active = true;
+ $hook->add();
+
+ $bearerToken = $this->getBearerToken([
+ 'hook_read',
+ 'hook_write',
+ ]);
+
+ $response = static::createClient()->request('GET', '/api/hooks/' . (int) $hook->id, ['auth_bearer' => $bearerToken]);
+ self::assertEquals(json_decode($response->getContent())->active, $hook->active);
+ self::assertResponseStatusCodeSame(200);
+
+ static::createClient()->request('GET', '/api/hooks/' . 9999, ['auth_bearer' => $bearerToken]);
+ self::assertResponseStatusCodeSame(404);
+
+ static::createClient()->request('GET', '/api/hooks/' . $hook->id);
+ self::assertResponseStatusCodeSame(401);
+
+ $hook->delete();
+ }
+}
diff --git a/tests/Integration/bootstrap.php b/tests/Integration/bootstrap.php
new file mode 100644
index 0000000..2246967
--- /dev/null
+++ b/tests/Integration/bootstrap.php
@@ -0,0 +1,42 @@
+
+ * @copyright Since 2007 PrestaShop SA and Contributors
+ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ */
+define('_PS_IN_TEST_', true);
+define('_PS_API_FORCE_TLS_VERSION_', false);
+define('_PS_ROOT_DIR_', dirname(__DIR__, 4));
+define('_PS_MODULE_DIR_', _PS_ROOT_DIR_ . '/tests/Resources/modules/');
+require_once dirname(__DIR__, 4) . '/vendor/smarty/smarty/libs/functions.php';
+require_once dirname(__DIR__, 4) . '/admin-dev/bootstrap.php';
+
+/*
+ * Following code makes tests run under phpstorm
+ * Else we get error : Class 'PHPUnit_Util_Configuration' not found
+ * @see https://stackoverflow.com/questions/33299149/phpstorm-8-and-phpunit-problems-with-runinseparateprocess
+ */
+if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
+ define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__, 4) . '/vendor/autoload.php');
+}
+
+require_once(dirname(__DIR__, 2) . '/vendor/autoload.php');
\ No newline at end of file
diff --git a/tests/Integration/phpunit.xml b/tests/Integration/phpunit.xml
new file mode 100644
index 0000000..5611864
--- /dev/null
+++ b/tests/Integration/phpunit.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+ isolatedProcess
+
+
+