From 58b83467bf7430ecfe30219bf51181f79affaa50 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Mon, 24 Feb 2025 16:36:00 +0400 Subject: [PATCH 01/13] ISSUE-344: api doc --- composer.json | 5 +- docs/.gitkeep | 0 openapi.yaml | 55 ------------------- src/Controller/ListController.php | 6 +- src/Controller/SubscriberController.php | 16 +++--- .../Controller/ListControllerTest.php | 12 ++-- .../Controller/SubscriberControllerTest.php | 14 ++--- 7 files changed, 22 insertions(+), 86 deletions(-) create mode 100644 docs/.gitkeep delete mode 100644 openapi.yaml diff --git a/composer.json b/composer.json index a613d5c..b7b28cd 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^8.1", - "phplist/core": "dev-ISSUE-337", + "phplist/core": "5.0.0-alpha1", "friendsofsymfony/rest-bundle": "*", "symfony/test-pack": "^1.0", "symfony/process": "^6.4", @@ -85,9 +85,6 @@ ] }, "extra": { - "branch-alias": { - "dev-ISSUE-337": "5.0.x-dev" - }, "symfony-app-dir": "bin", "symfony-bin-dir": "bin", "symfony-var-dir": "var", diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/openapi.yaml b/openapi.yaml deleted file mode 100644 index b7a3b0e..0000000 --- a/openapi.yaml +++ /dev/null @@ -1,55 +0,0 @@ -openapi: 3.0.0 -info: - title: 'My API Documentation' - description: 'This is the OpenAPI documentation for My API.' - contact: - email: support@example.com - license: - name: MIT - url: 'https://opensource.org/licenses/MIT' - version: 1.0.0 -servers: - - - url: 'https://api.example.com' - description: 'Production server' - - - url: 'https://staging-api.example.com' - description: 'Staging server' -paths: - /api/v2/lists: - get: - tags: - - lists - summary: 'Gets a list of all subscriber lists.' - description: 'Returns a JSON list of all subscriber lists.' - operationId: 88f205a115c9d929147a83720a247aae - parameters: - - - name: session - in: header - description: 'Session ID obtained from authentication' - required: true - schema: - type: string - responses: - '200': - description: Success - content: - application/json: - schema: - type: array - items: - properties: { name: { type: string, example: News }, description: { type: string, example: 'News (and some fun stuff)' }, creation_date: { type: string, format: date-time, example: '2016-06-22T15:01:17+00:00' }, list_position: { type: integer, example: 12 }, subject_prefix: { type: string, example: phpList }, public: { type: boolean, example: true }, category: { type: string, example: news }, id: { type: integer, example: 1 } } - type: object - '403': - description: Failure - content: - application/json: - schema: - properties: - message: { type: string, example: 'No valid session key was provided as basic auth password.' } - type: object -tags: - - - name: lists - description: lists diff --git a/src/Controller/ListController.php b/src/Controller/ListController.php index ccac0f9..9664a3c 100644 --- a/src/Controller/ListController.php +++ b/src/Controller/ListController.php @@ -267,11 +267,11 @@ public function deleteList( return new JsonResponse(null, Response::HTTP_NO_CONTENT, [], false); } - #[Route('/lists/{id}/members', name: 'get_subscriber_from_list', methods: ['GET'])] + #[Route('/lists/{id}/subscribers', name: 'get_subscriber_from_list', methods: ['GET'])] #[OA\Get( - path: '/lists/{list}/members', + path: '/lists/{list}/subscribers', description: 'Returns a JSON list of all subscribers for a subscriber list.', - summary: 'Gets a list of all subscribers (members) of a subscriber list.', + summary: 'Gets a list of all subscribers of a subscriber list.', tags: ['lists'], parameters: [ new OA\Parameter( diff --git a/src/Controller/SubscriberController.php b/src/Controller/SubscriberController.php index 3d9087b..55d32e4 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -39,7 +39,7 @@ public function __construct( #[Route('/subscribers', name: 'create_subscriber', methods: ['POST'])] #[OA\Post( - path: '/subscriber', + path: '/subscribers', description: 'Creates a new subscriber (if there is no subscriber with the given email address yet).', summary: 'Create a subscriber', requestBody: new OA\RequestBody( @@ -49,10 +49,8 @@ public function __construct( required: ['email'], properties: [ new OA\Property(property: 'email', type: 'string', format: 'string', example: 'admin@example.com'), - new OA\Property(property: 'confirmed', type: 'boolean', example: false), - new OA\Property(property: 'blacklisted', type: 'boolean', example: false), + new OA\Property(property: 'request_confirmation', type: 'boolean', example: false), new OA\Property(property: 'html_email', type: 'boolean', example: false), - new OA\Property(property: 'disabled', type: 'boolean', example: false) ] ) ), @@ -140,13 +138,13 @@ public function postAction(Request $request, SerializerInterface $serializer): J if ($this->subscriberRepository->findOneByEmail($email) !== null) { throw new ConflictHttpException('This resource already exists.', null, 1513439108); } - // @phpstan-ignore-next-line + $confirmed = (bool)$data->get('request_confirmation', true); $subscriber = new Subscriber(); $subscriber->setEmail($email); - $subscriber->setConfirmed((bool)$data->get('confirmed', false)); - $subscriber->setBlacklisted((bool)$data->get('blacklisted', false)); + $subscriber->setConfirmed(!$confirmed); + $subscriber->setBlacklisted(false); $subscriber->setHtmlEmail((bool)$data->get('html_email', true)); - $subscriber->setDisabled((bool)$data->get('disabled', false)); + $subscriber->setDisabled(false); $this->subscriberRepository->save($subscriber); @@ -173,7 +171,7 @@ private function validateSubscriber(Request $request): void $invalidFields[] = 'email'; } - $booleanFields = ['confirmed', 'blacklisted', 'html_email', 'disabled']; + $booleanFields = ['request_confirmation', 'html_email']; foreach ($booleanFields as $fieldKey) { if ($request->getPayload()->get($fieldKey) !== null && !is_bool($request->getPayload()->get($fieldKey)) diff --git a/tests/Integration/Controller/ListControllerTest.php b/tests/Integration/Controller/ListControllerTest.php index e12f4e2..516bc48 100644 --- a/tests/Integration/Controller/ListControllerTest.php +++ b/tests/Integration/Controller/ListControllerTest.php @@ -180,7 +180,7 @@ public function testGetListMembersForExistingListWithoutSessionKeyReturnsForbidd { $this->loadFixtures([SubscriberListFixture::class]); - self::getClient()->request('get', '/api/v2/lists/1/members'); + self::getClient()->request('get', '/api/v2/lists/1/subscribers'); $this->assertHttpForbidden(); } @@ -195,7 +195,7 @@ public function testGetListMembersForExistingListWithExpiredSessionKeyReturnsFor self::getClient()->request( 'get', - '/api/v2/lists/1/members', + '/api/v2/lists/1/subscribers', [], [], ['PHP_AUTH_USER' => 'unused', 'PHP_AUTH_PW' => 'cfdf64eecbbf336628b0f3071adba763'] @@ -206,7 +206,7 @@ public function testGetListMembersForExistingListWithExpiredSessionKeyReturnsFor public function testGetListMembersWithCurrentSessionKeyForInexistentListReturnsNotFoundStatus() { - $this->authenticatedJsonRequest('get', '/api/v2/lists/999/members'); + $this->authenticatedJsonRequest('get', '/api/v2/lists/999/subscribers'); $this->assertHttpNotFound(); } @@ -215,7 +215,7 @@ public function testGetListMembersWithCurrentSessionKeyForExistingListReturnsOka { $this->loadFixtures([SubscriberListFixture::class]); - $this->authenticatedJsonRequest('get', '/api/v2/lists/1/members'); + $this->authenticatedJsonRequest('get', '/api/v2/lists/1/subscribers'); $this->assertHttpOkay(); } @@ -224,7 +224,7 @@ public function testGetListMembersWithCurrentSessionKeyForExistingListWithoutSub { $this->loadFixtures([SubscriberListFixture::class]); - $this->authenticatedJsonRequest('get', '/api/v2/lists/1/members'); + $this->authenticatedJsonRequest('get', '/api/v2/lists/1/subscribers'); $this->assertJsonResponseContentEquals([]); } @@ -233,7 +233,7 @@ public function testGetListMembersWithCurrentSessionKeyForExistingListWithSubscr { $this->loadFixtures([SubscriberListFixture::class, SubscriberFixture::class, SubscriptionFixture::class]); - $this->authenticatedJsonRequest('get', '/api/v2/lists/2/members'); + $this->authenticatedJsonRequest('get', '/api/v2/lists/2/subscribers'); $this->assertJsonResponseContentEquals( [ diff --git a/tests/Integration/Controller/SubscriberControllerTest.php b/tests/Integration/Controller/SubscriberControllerTest.php index 21a63ea..8c34c77 100644 --- a/tests/Integration/Controller/SubscriberControllerTest.php +++ b/tests/Integration/Controller/SubscriberControllerTest.php @@ -106,14 +106,10 @@ public static function invalidSubscriberDataProvider(): array 'email is an empty string' => [['email' => '']], 'email is invalid string' => [['email' => 'coffee and cigarettes']], 'email as boolean' => [['email' => true]], - 'confirmed as integer' => [['email' => 'kate@example.com', 'confirmed' => 1]], - 'confirmed as string' => [['email' => 'kate@example.com', 'confirmed' => 'yes']], - 'blacklisted as integer' => [['email' => 'kate@example.com', 'blacklisted' => 1]], - 'blacklisted as string' => [['email' => 'kate@example.com', 'blacklisted' => 'yes']], 'html_email as integer' => [['email' => 'kate@example.com', 'html_email' => 1]], 'html_email as string' => [['email' => 'kate@example.com', 'html_email' => 'yes']], - 'disabled as integer' => [['email' => 'kate@example.com', 'disabled' => 1]], - 'disabled as string' => [['email' => 'kate@example.com', 'disabled' => 'yes']], + 'request_confirmation as string' => [['email' => 'kate@example.com', 'request_confirmation' => 'needed']], + 'disabled as string' => [['email' => 'kate@example.com', 'request_confirmation' => 1]], ]; } @@ -144,9 +140,9 @@ public function testPostSubscribersWithValidSessionKeyAssignsProvidedSubscriberD $responseContent = $this->getDecodedJsonResponseContent(); static::assertSame($email, $responseContent['email']); - static::assertTrue($responseContent['confirmed']); - static::assertTrue($responseContent['blacklisted']); + static::assertFalse($responseContent['confirmed']); + static::assertFalse($responseContent['blacklisted']); static::assertTrue($responseContent['html_email']); - static::assertTrue($responseContent['disabled']); + static::assertFalse($responseContent['disabled']); } } From 76360d60fe86611db41e96e78c5a56c372a28d67 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Tue, 25 Feb 2025 21:12:38 +0400 Subject: [PATCH 02/13] ISSUE-344: update core version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b7b28cd..7b68e4a 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^8.1", - "phplist/core": "5.0.0-alpha1", + "phplist/core": "5.0.0-alpha2", "friendsofsymfony/rest-bundle": "*", "symfony/test-pack": "^1.0", "symfony/process": "^6.4", From bf669e57b8a887ae6bf5cbc82e80f91ab306792d Mon Sep 17 00:00:00 2001 From: Tatevik Date: Tue, 25 Feb 2025 21:55:18 +0400 Subject: [PATCH 03/13] ISSUE-344: add get subscriber by id route --- src/Controller/ListController.php | 8 +-- src/Controller/SubscriberController.php | 84 +++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/Controller/ListController.php b/src/Controller/ListController.php index 9664a3c..16241be 100644 --- a/src/Controller/ListController.php +++ b/src/Controller/ListController.php @@ -269,7 +269,7 @@ public function deleteList( #[Route('/lists/{id}/subscribers', name: 'get_subscriber_from_list', methods: ['GET'])] #[OA\Get( - path: '/lists/{list}/subscribers', + path: '/lists/{id}/subscribers', description: 'Returns a JSON list of all subscribers for a subscriber list.', summary: 'Gets a list of all subscribers of a subscriber list.', tags: ['lists'], @@ -282,7 +282,7 @@ public function deleteList( schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'list', + name: 'id', description: 'List ID', in: 'path', required: true, @@ -353,7 +353,7 @@ public function getListMembers( #[Route('/lists/{id}/subscribers/count', name: 'get_subscribers_count_from_list', methods: ['GET'])] #[OA\Get( - path: '/lists/{list}/count', + path: '/lists/{id}/count', description: 'Returns a count of all subscribers in a given list.', summary: 'Gets the total number of subscribers of a list', tags: ['lists'], @@ -366,7 +366,7 @@ public function getListMembers( schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'list', + name: 'id', description: 'List ID', in: 'path', required: true, diff --git a/src/Controller/SubscriberController.php b/src/Controller/SubscriberController.php index 55d32e4..df7dcec 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Controller; +use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use PhpList\Core\Domain\Model\Subscription\Subscriber; use PhpList\Core\Domain\Repository\Subscription\SubscriberRepository; @@ -15,6 +16,7 @@ use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\SerializerInterface; use OpenApi\Attributes as OA; @@ -156,6 +158,88 @@ public function postAction(Request $request, SerializerInterface $serializer): J ); } + #[Route('/subscribers/{subscriber}', name: 'get_subscriber_by_id', methods: ['GET'])] + #[OA\Get( + path: '/subscribers/{subscriber}', + description: 'Get subscriber date by id.', + summary: 'Get a subscriber', + tags: ['subscribers'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'id', + description: 'Subscriber ID', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'creation_date', + type: 'string', + format: 'date-time', + example: '2017-12-16T18:44:27+00:00' + ), + new OA\Property(property: 'email', type: 'string', example: 'subscriber@example.com'), + new OA\Property(property: 'confirmed', type: 'boolean', example: false), + new OA\Property(property: 'blacklisted', type: 'boolean', example: false), + new OA\Property(property: 'bounced', type: 'integer', example: 0), + new OA\Property( + property: 'unique_id', + type: 'string', + example: '69f4e92cf50eafca9627f35704f030f4' + ), + new OA\Property(property: 'html_email', type: 'boolean', example: false), + new OA\Property(property: 'disabled', type: 'boolean', example: false), + new OA\Property(property: 'id', type: 'integer', example: 1) + ] + ) + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'message', + type: 'string', + example: 'No valid session key was provided as basic auth password.' + ) + ] + ) + ), + new OA\Response( + response: 404, + description: 'Not Found', + ) + ] + )] + public function getAction( + Request $request, + #[MapEntity(mapping: ['id' => 'id'])] Subscriber $subscriber, + SerializerInterface $serializer + ): JsonResponse { + $this->requireAuthentication($request); + + $json = $serializer->serialize($subscriber, 'json', [ + AbstractNormalizer::GROUPS => 'SubscriberListMembers', + ]); + + return new JsonResponse($json, Response::HTTP_OK, [], true); + } + /** * @param Request $request * From 5e5002768736eb7f0b1538d42b58ce12769d4a02 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 2 Mar 2025 19:57:02 +0400 Subject: [PATCH 04/13] ISSUE-344: add docs workflow --- .github/workflows/restapi-docs.yml | 93 ++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/workflows/restapi-docs.yml diff --git a/.github/workflows/restapi-docs.yml b/.github/workflows/restapi-docs.yml new file mode 100644 index 0000000..7d5c741 --- /dev/null +++ b/.github/workflows/restapi-docs.yml @@ -0,0 +1,93 @@ +name: Publish REST API Docs +on: + push: + branches: + - main + pull_request: + +jobs: + make-restapi-docs: + name: Checkout phpList rest-api and generate docs specification (OpenAPI latest-restapi.json) + runs-on: ubuntu-20.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Setup PHP with Composer and Extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: mbstring, dom, fileinfo, mysql + + - name: Cache Composer Dependencies + uses: actions/cache@v3 + with: + path: ~/.composer/cache + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install Composer Dependencies + run: composer install --no-interaction --prefer-dist + + - name: Generate OpenAPI Specification JSON + run: vendor/bin/openapi -o docs/latest-restapi.json --format json src + + - name: Upload REST API Specification + uses: actions/upload-artifact@v4 + with: + name: restapi-json + path: docs/latest-restapi.json + + deploy-docs: + name: Deploy REST API Specification + runs-on: ubuntu-20.04 + needs: make-restapi-docs + steps: + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 14 + + - name: Install openapi-checker + run: npm install -g swagger-cli + + - name: Checkout REST API Docs Repository + uses: actions/checkout@v3 + with: + repository: phpList/restapi-docs + fetch-depth: 0 + token: ${{ secrets.PUSH_REST_API_DOCS }} + + - name: Download Generated REST API Specification + uses: actions/download-artifact@v4 + with: + name: restapi-json + path: docs + + - name: Validate OpenAPI Specification + run: swagger-cli validate docs/latest-restapi.json + + - name: Compare Specifications + run: git diff --no-index --output=restapi-diff.txt docs/latest-restapi.json restapi.json || true + + - name: Check Differences and Decide Deployment + id: allow-deploy + run: | + if [ -s restapi-diff.txt ]; then + echo "Updates detected in the REST API specification. Proceeding with deployment."; + echo 'DEPLOY=true' >> $GITHUB_ENV; + else + echo "No changes detected in the REST API specification. Skipping deployment."; + echo 'DEPLOY=false' >> $GITHUB_ENV; + fi + + - name: Commit and Deploy Updates + if: env.DEPLOY == 'true' + run: | + mv docs/latest-restapi.json docs/restapi.json + git config user.name "github-actions" + git config user.email "github-actions@restapi-docs.workflow" + git add docs/restapi.json + git commit -m "Update REST API documentation `date`" + git push From 5596e2f66b0b7692786484ccc860eeea6e76dd95 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 2 Mar 2025 20:44:09 +0400 Subject: [PATCH 05/13] ISSUE-344: install from branch --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7b68e4a..7d8ad82 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^8.1", - "phplist/core": "5.0.0-alpha2", + "phplist/core": "dev-ISSUE-344", "friendsofsymfony/rest-bundle": "*", "symfony/test-pack": "^1.0", "symfony/process": "^6.4", From 510f0670f0e1b9ae2ad1316e2ac8c4e983741b17 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Mon, 3 Mar 2025 21:18:40 +0400 Subject: [PATCH 06/13] ISSUE-344: fix parameter names --- src/Controller/ListController.php | 37 +++++++++++++------------ src/Controller/SessionController.php | 8 +++--- src/Controller/SubscriberController.php | 8 +++--- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/Controller/ListController.php b/src/Controller/ListController.php index 16241be..f16028d 100644 --- a/src/Controller/ListController.php +++ b/src/Controller/ListController.php @@ -24,6 +24,7 @@ * * @author Oliver Klee * @author Xheni Myrtaj + * @author Tatevik Grigoryan */ class ListController extends AbstractController { @@ -119,15 +120,15 @@ public function getLists(Request $request): JsonResponse return new JsonResponse($json, Response::HTTP_OK, [], true); } - #[Route('/lists/{id}', name: 'get_list', methods: ['GET'])] + #[Route('/lists/{listId}', name: 'get_list', methods: ['GET'])] #[OA\Get( - path: '/lists/{list}', + path: '/lists/{listId}', description: 'Returns a single subscriber list with specified ID.', summary: 'Gets a subscriber list.', tags: ['lists'], parameters: [ new OA\Parameter( - name: 'list', + name: 'listId', description: 'List ID', in: 'path', required: true, @@ -189,8 +190,10 @@ public function getLists(Request $request): JsonResponse ) ] )] - public function getList(Request $request, #[MapEntity(mapping: ['id' => 'id'])] SubscriberList $list): JsonResponse - { + public function getList( + Request $request, + #[MapEntity(mapping: ['listId' => 'id'])] SubscriberList $list + ): JsonResponse { $this->requireAuthentication($request); $json = $this->serializer->serialize($list, 'json', [ AbstractNormalizer::GROUPS => 'SubscriberList', @@ -199,9 +202,9 @@ public function getList(Request $request, #[MapEntity(mapping: ['id' => 'id'])] return new JsonResponse($json, Response::HTTP_OK, [], true); } - #[Route('/lists/{id}', name: 'delete_list', methods: ['DELETE'])] + #[Route('/lists/{listId}', name: 'delete_list', methods: ['DELETE'])] #[OA\Delete( - path: '/lists/{list}', + path: '/lists/{listId}', description: 'Deletes a single subscriber list.', summary: 'Deletes a list.', tags: ['lists'], @@ -214,7 +217,7 @@ public function getList(Request $request, #[MapEntity(mapping: ['id' => 'id'])] schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'list', + name: 'listId', description: 'List ID', in: 'path', required: true, @@ -258,7 +261,7 @@ public function getList(Request $request, #[MapEntity(mapping: ['id' => 'id'])] )] public function deleteList( Request $request, - #[MapEntity(mapping: ['id' => 'id'])] SubscriberList $list + #[MapEntity(mapping: ['listId' => 'id'])] SubscriberList $list ): JsonResponse { $this->requireAuthentication($request); @@ -267,9 +270,9 @@ public function deleteList( return new JsonResponse(null, Response::HTTP_NO_CONTENT, [], false); } - #[Route('/lists/{id}/subscribers', name: 'get_subscriber_from_list', methods: ['GET'])] + #[Route('/lists/{listId}/subscribers', name: 'get_subscriber_from_list', methods: ['GET'])] #[OA\Get( - path: '/lists/{id}/subscribers', + path: '/lists/{listId}/subscribers', description: 'Returns a JSON list of all subscribers for a subscriber list.', summary: 'Gets a list of all subscribers of a subscriber list.', tags: ['lists'], @@ -282,7 +285,7 @@ public function deleteList( schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'id', + name: 'listId', description: 'List ID', in: 'path', required: true, @@ -338,7 +341,7 @@ public function deleteList( )] public function getListMembers( Request $request, - #[MapEntity(mapping: ['id' => 'id'])] SubscriberList $list + #[MapEntity(mapping: ['listId' => 'id'])] SubscriberList $list ): JsonResponse { $this->requireAuthentication($request); @@ -351,9 +354,9 @@ public function getListMembers( return new JsonResponse($json, Response::HTTP_OK, [], true); } - #[Route('/lists/{id}/subscribers/count', name: 'get_subscribers_count_from_list', methods: ['GET'])] + #[Route('/lists/{listId}/subscribers/count', name: 'get_subscribers_count_from_list', methods: ['GET'])] #[OA\Get( - path: '/lists/{id}/count', + path: '/lists/{listId}/count', description: 'Returns a count of all subscribers in a given list.', summary: 'Gets the total number of subscribers of a list', tags: ['lists'], @@ -366,7 +369,7 @@ public function getListMembers( schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'id', + name: 'listId', description: 'List ID', in: 'path', required: true, @@ -396,7 +399,7 @@ public function getListMembers( )] public function getSubscribersCount( Request $request, - #[MapEntity(mapping: ['id' => 'id'])] SubscriberList $list + #[MapEntity(mapping: ['listId' => 'id'])] SubscriberList $list ): JsonResponse { $this->requireAuthentication($request); $json = $this->serializer->serialize(count($list->getSubscribers()), 'json'); diff --git a/src/Controller/SessionController.php b/src/Controller/SessionController.php index 1c12dd0..4509e67 100644 --- a/src/Controller/SessionController.php +++ b/src/Controller/SessionController.php @@ -129,15 +129,15 @@ public function createSession(Request $request): JsonResponse * * @throws AccessDeniedHttpException */ - #[Route('/sessions/{id}', name: 'delete_session', methods: ['DELETE'])] + #[Route('/sessions/{sessionId}', name: 'delete_session', methods: ['DELETE'])] #[OA\Delete( - path: '/sessions/{session}', + path: '/sessions/{sessionId}', description: 'Delete the session passed as a parameter.', summary: 'Delete a session.', tags: ['sessions'], parameters: [ new OA\Parameter( - name: 'session', + name: 'sessionId', description: 'Session ID', in: 'path', required: true, @@ -179,7 +179,7 @@ public function createSession(Request $request): JsonResponse )] public function deleteAction( Request $request, - #[MapEntity(mapping: ['id' => 'id'])] AdministratorToken $token + #[MapEntity(mapping: ['sessionId' => 'id'])] AdministratorToken $token ): JsonResponse { $administrator = $this->requireAuthentication($request); if ($token->getAdministrator() !== $administrator) { diff --git a/src/Controller/SubscriberController.php b/src/Controller/SubscriberController.php index df7dcec..036955d 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -158,9 +158,9 @@ public function postAction(Request $request, SerializerInterface $serializer): J ); } - #[Route('/subscribers/{subscriber}', name: 'get_subscriber_by_id', methods: ['GET'])] + #[Route('/subscribers/{subscriberId}', name: 'get_subscriber_by_id', methods: ['GET'])] #[OA\Get( - path: '/subscribers/{subscriber}', + path: '/subscribers/{subscriberId}', description: 'Get subscriber date by id.', summary: 'Get a subscriber', tags: ['subscribers'], @@ -173,7 +173,7 @@ public function postAction(Request $request, SerializerInterface $serializer): J schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'id', + name: 'subscriberId', description: 'Subscriber ID', in: 'path', required: true, @@ -228,7 +228,7 @@ public function postAction(Request $request, SerializerInterface $serializer): J )] public function getAction( Request $request, - #[MapEntity(mapping: ['id' => 'id'])] Subscriber $subscriber, + #[MapEntity(mapping: ['subscriberId' => 'id'])] Subscriber $subscriber, SerializerInterface $serializer ): JsonResponse { $this->requireAuthentication($request); From 073556b9c15954250aad058e7d00740afad8ba16 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 5 Mar 2025 19:38:49 +0400 Subject: [PATCH 07/13] ISSUE-344: refactor --- config/services.yml | 29 +++++++++----------- config/services/controllers.yml | 7 +++++ config/services/listeners.yml | 8 ++++++ config/services/serializer.yml | 5 ++++ src/Controller/SubscriberController.php | 20 +++++++++----- src/Serializer/SubscriberNormalizer.php | 36 +++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 23 deletions(-) create mode 100644 config/services/controllers.yml create mode 100644 config/services/listeners.yml create mode 100644 config/services/serializer.yml create mode 100644 src/Serializer/SubscriberNormalizer.php diff --git a/config/services.yml b/config/services.yml index 4aede00..d70ed68 100644 --- a/config/services.yml +++ b/config/services.yml @@ -1,3 +1,6 @@ +imports: + - { resource: services/*.xml } + services: Psr\Container\ContainerInterface: alias: 'service_container' @@ -6,14 +9,19 @@ services: resource: '../src/Controller' public: true autowire: true - tags: ['controller.service_arguments'] + autoconfigure: true -# Symfony\Component\Serializer\SerializerInterface: -# autowire: true -# autoconfigure: true + PhpList\RestBundle\: + resource: '../src/' + exclude: + - '../src/Controller/' + - '../src/DependencyInjection/' + - '../src/Kernel.php' + autowire: true + autoconfigure: true my.secure_handler: - class: \PhpList\RestBundle\ViewHandler\SecuredViewHandler + class: PhpList\RestBundle\ViewHandler\SecuredViewHandler my.secure_view_handler: parent: fos_rest.view_handler.default @@ -24,14 +32,3 @@ services: autowire: true autoconfigure: true - PhpList\Core\Domain\Repository\Messaging\SubscriberListRepository: - autowire: true - autoconfigure: true - - PhpList\RestBundle\EventListener\ExceptionListener: - tags: - - { name: kernel.event_listener, event: kernel.exception } - - PhpList\RestBundle\EventListener\ResponseListener: - tags: - - { name: kernel.event_listener, event: kernel.response } diff --git a/config/services/controllers.yml b/config/services/controllers.yml new file mode 100644 index 0000000..db979b5 --- /dev/null +++ b/config/services/controllers.yml @@ -0,0 +1,7 @@ +services: + PhpList\RestBundle\Controller\SubscriberController: + autowire: true + arguments: + $authentication: '@PhpList\Core\Security\Authentication' + $repository: '@PhpList\Core\Domain\Repository\Subscription\SubscriberRepository' + $subscriberNormalizer: '@PhpList\RestBundle\Serializer\SubscriberNormalizer' diff --git a/config/services/listeners.yml b/config/services/listeners.yml new file mode 100644 index 0000000..eff5664 --- /dev/null +++ b/config/services/listeners.yml @@ -0,0 +1,8 @@ +services: + PhpList\RestBundle\EventListener\ExceptionListener: + tags: + - { name: kernel.event_listener, event: kernel.exception } + + PhpList\RestBundle\EventListener\ResponseListener: + tags: + - { name: kernel.event_listener, event: kernel.response } diff --git a/config/services/serializer.yml b/config/services/serializer.yml new file mode 100644 index 0000000..b8400b3 --- /dev/null +++ b/config/services/serializer.yml @@ -0,0 +1,5 @@ +services: + PhpList\RestBundle\Serializer\SubscriberNormalizer: + tags: ['serializer.normalizer'] + autowire: true + diff --git a/src/Controller/SubscriberController.php b/src/Controller/SubscriberController.php index 036955d..762b2a2 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Controller; +use PhpList\RestBundle\Serializer\SubscriberNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use PhpList\Core\Domain\Model\Subscription\Subscriber; @@ -16,7 +17,6 @@ use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\SerializerInterface; use OpenApi\Attributes as OA; @@ -30,13 +30,16 @@ class SubscriberController extends AbstractController use AuthenticationTrait; private SubscriberRepository $subscriberRepository; + private SubscriberNormalizer $subscriberNormalizer; public function __construct( Authentication $authentication, - SubscriberRepository $repository + SubscriberRepository $repository, + SubscriberNormalizer $subscriberNormalizer ) { $this->authentication = $authentication; $this->subscriberRepository = $repository; + $this->subscriberNormalizer = $subscriberNormalizer; } #[Route('/subscribers', name: 'create_subscriber', methods: ['POST'])] @@ -229,15 +232,18 @@ public function postAction(Request $request, SerializerInterface $serializer): J public function getAction( Request $request, #[MapEntity(mapping: ['subscriberId' => 'id'])] Subscriber $subscriber, - SerializerInterface $serializer ): JsonResponse { $this->requireAuthentication($request); - $json = $serializer->serialize($subscriber, 'json', [ - AbstractNormalizer::GROUPS => 'SubscriberListMembers', - ]); + $subscriber = $this->subscriberRepository->findSubscriberWithSubscriptions($subscriber->getId()); - return new JsonResponse($json, Response::HTTP_OK, [], true); + if (!$subscriber) { + return new JsonResponse(['error' => 'Subscriber not found'], Response::HTTP_NOT_FOUND); + } + + $data = $this->subscriberNormalizer->normalize($subscriber); + + return new JsonResponse($data, Response::HTTP_OK, []); } /** diff --git a/src/Serializer/SubscriberNormalizer.php b/src/Serializer/SubscriberNormalizer.php new file mode 100644 index 0000000..fd0c32c --- /dev/null +++ b/src/Serializer/SubscriberNormalizer.php @@ -0,0 +1,36 @@ + $object->getId(), + 'email' => $object->getEmail(), + 'subscribedLists' => array_map(function ($subscription) { + + return [ + 'id' => $subscription->getSubscriberList()->getId(), + 'name' => $subscription->getSubscriberList()->getName(), + ]; + }, $object->getSubscriptions()->toArray()), + ]; + } + + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof Subscriber; + } +} + From d24844dd074afa8cb9b9632c603427cc835ed06e Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 5 Mar 2025 19:54:32 +0400 Subject: [PATCH 08/13] ISSUE-344: update action version --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7e05c6..ffb8533 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: dependencies: ['latest', 'oldest'] steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php with: @@ -41,7 +41,7 @@ jobs: id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" - name: Cache composer dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} # Use composer.json for key, if composer.lock is not committed. From 8fd213d87682f5280f6bd71269d8cbf40bad531a Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 5 Mar 2025 20:32:00 +0400 Subject: [PATCH 09/13] ISSUE-344: update configs --- config/services.yml | 32 +++++++++++++++++++------------- config/services/controllers.yml | 7 ------- config/services/listeners.yml | 8 -------- config/services/serializer.yml | 5 ----- 4 files changed, 19 insertions(+), 33 deletions(-) delete mode 100644 config/services/controllers.yml delete mode 100644 config/services/listeners.yml delete mode 100644 config/services/serializer.yml diff --git a/config/services.yml b/config/services.yml index d70ed68..36264ec 100644 --- a/config/services.yml +++ b/config/services.yml @@ -1,24 +1,16 @@ -imports: - - { resource: services/*.xml } - services: Psr\Container\ContainerInterface: - alias: 'service_container' + alias: 'service_container' PhpList\RestBundle\Controller\: resource: '../src/Controller' public: true autowire: true - autoconfigure: true + tags: ['controller.service_arguments'] - PhpList\RestBundle\: - resource: '../src/' - exclude: - - '../src/Controller/' - - '../src/DependencyInjection/' - - '../src/Kernel.php' - autowire: true - autoconfigure: true + # Symfony\Component\Serializer\SerializerInterface: + # autowire: true + # autoconfigure: true my.secure_handler: class: PhpList\RestBundle\ViewHandler\SecuredViewHandler @@ -32,3 +24,17 @@ services: autowire: true autoconfigure: true + PhpList\Core\Domain\Repository\Messaging\SubscriberListRepository: + autowire: true + autoconfigure: true + + PhpList\RestBundle\EventListener\ExceptionListener: + tags: + - { name: kernel.event_listener, event: kernel.exception } + + PhpList\RestBundle\EventListener\ResponseListener: + tags: + - { name: kernel.event_listener, event: kernel.response } + PhpList\RestBundle\Serializer\SubscriberNormalizer: + tags: [ 'serializer.normalizer' ] + autowire: true diff --git a/config/services/controllers.yml b/config/services/controllers.yml deleted file mode 100644 index db979b5..0000000 --- a/config/services/controllers.yml +++ /dev/null @@ -1,7 +0,0 @@ -services: - PhpList\RestBundle\Controller\SubscriberController: - autowire: true - arguments: - $authentication: '@PhpList\Core\Security\Authentication' - $repository: '@PhpList\Core\Domain\Repository\Subscription\SubscriberRepository' - $subscriberNormalizer: '@PhpList\RestBundle\Serializer\SubscriberNormalizer' diff --git a/config/services/listeners.yml b/config/services/listeners.yml deleted file mode 100644 index eff5664..0000000 --- a/config/services/listeners.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - PhpList\RestBundle\EventListener\ExceptionListener: - tags: - - { name: kernel.event_listener, event: kernel.exception } - - PhpList\RestBundle\EventListener\ResponseListener: - tags: - - { name: kernel.event_listener, event: kernel.response } diff --git a/config/services/serializer.yml b/config/services/serializer.yml deleted file mode 100644 index b8400b3..0000000 --- a/config/services/serializer.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - PhpList\RestBundle\Serializer\SubscriberNormalizer: - tags: ['serializer.normalizer'] - autowire: true - From 061342e4b7b51fc2eee7811abc46e549c65dd84b Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 5 Mar 2025 20:56:23 +0400 Subject: [PATCH 10/13] ISSUE-344: add fields in normalizer --- src/Controller/ListController.php | 35 ++++++++--- src/Controller/SubscriberController.php | 61 +++++++++++-------- src/Serializer/SubscriberNormalizer.php | 14 ++++- .../Controller/ListControllerTest.php | 33 ++++++++-- 4 files changed, 100 insertions(+), 43 deletions(-) diff --git a/src/Controller/ListController.php b/src/Controller/ListController.php index f16028d..6b05716 100644 --- a/src/Controller/ListController.php +++ b/src/Controller/ListController.php @@ -300,24 +300,39 @@ public function deleteList( type: 'array', items: new OA\Items( properties: [ + new OA\Property(property: 'id', type: 'integer', example: 1), + new OA\Property(property: 'email', type: 'string', example: 'subscriber@example.com'), new OA\Property( property: 'creation_date', type: 'string', format: 'date-time', - example: '2016-07-22T15:01:17+00:00' + example: '2023-01-01T12:00:00Z' ), - new OA\Property(property: 'email', type: 'string', example: 'oliver@example.com'), new OA\Property(property: 'confirmed', type: 'boolean', example: true), - new OA\Property(property: 'blacklisted', type: 'boolean', example: true), - new OA\Property(property: 'bounce_count', type: 'integer', example: 17), + new OA\Property(property: 'blacklisted', type: 'boolean', example: false), + new OA\Property(property: 'bounce_count', type: 'integer', example: 0), + new OA\Property(property: 'unique_id', type: 'string', example: 'abc123'), + new OA\Property(property: 'html_email', type: 'boolean', example: true), + new OA\Property(property: 'disabled', type: 'boolean', example: false), new OA\Property( - property: 'unique_id', - type: 'string', - example: '95feb7fe7e06e6c11ca8d0c48cb46e89' + property: 'subscribedLists', + type: 'array', + items: new OA\Items( + properties: [ + new OA\Property(property: 'id', type: 'integer', example: 2), + new OA\Property(property: 'name', type: 'string', example: 'Newsletter'), + new OA\Property(property: 'description', type: 'string', example: 'Monthly updates'), + new OA\Property( + property: 'creation_date', + type: 'string', + format: 'date-time', + example: '2022-12-01T10:00:00Z' + ), + new OA\Property(property: 'public', type: 'boolean', example: true), + ], + type: 'object' + ) ), - new OA\Property(property: 'html_email', type: 'boolean', example: true), - new OA\Property(property: 'disabled', type: 'boolean', example: true), - new OA\Property(property: 'id', type: 'integer', example: 1) ], type: 'object' ) diff --git a/src/Controller/SubscriberController.php b/src/Controller/SubscriberController.php index 762b2a2..f35d76c 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -4,8 +4,6 @@ namespace PhpList\RestBundle\Controller; -use PhpList\RestBundle\Serializer\SubscriberNormalizer; -use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use PhpList\Core\Domain\Model\Subscription\Subscriber; use PhpList\Core\Domain\Repository\Subscription\SubscriberRepository; @@ -30,16 +28,11 @@ class SubscriberController extends AbstractController use AuthenticationTrait; private SubscriberRepository $subscriberRepository; - private SubscriberNormalizer $subscriberNormalizer; - public function __construct( - Authentication $authentication, - SubscriberRepository $repository, - SubscriberNormalizer $subscriberNormalizer - ) { + public function __construct(Authentication $authentication, SubscriberRepository $repository) + { $this->authentication = $authentication; $this->subscriberRepository = $repository; - $this->subscriberNormalizer = $subscriberNormalizer; } #[Route('/subscribers', name: 'create_subscriber', methods: ['POST'])] @@ -189,25 +182,41 @@ public function postAction(Request $request, SerializerInterface $serializer): J description: 'Success', content: new OA\JsonContent( properties: [ + new OA\Property(property: 'id', type: 'integer', example: 1), + new OA\Property(property: 'email', type: 'string', example: 'subscriber@example.com'), new OA\Property( property: 'creation_date', type: 'string', format: 'date-time', - example: '2017-12-16T18:44:27+00:00' + example: '2023-01-01T12:00:00Z' ), - new OA\Property(property: 'email', type: 'string', example: 'subscriber@example.com'), - new OA\Property(property: 'confirmed', type: 'boolean', example: false), + new OA\Property(property: 'confirmed', type: 'boolean', example: true), new OA\Property(property: 'blacklisted', type: 'boolean', example: false), - new OA\Property(property: 'bounced', type: 'integer', example: 0), + new OA\Property(property: 'bounce_count', type: 'integer', example: 0), + new OA\Property(property: 'unique_id', type: 'string', example: 'abc123'), + new OA\Property(property: 'html_email', type: 'boolean', example: true), + new OA\Property(property: 'disabled', type: 'boolean', example: false), new OA\Property( - property: 'unique_id', - type: 'string', - example: '69f4e92cf50eafca9627f35704f030f4' + property: 'subscribedLists', + type: 'array', + items: new OA\Items( + properties: [ + new OA\Property(property: 'id', type: 'integer', example: 2), + new OA\Property(property: 'name', type: 'string', example: 'Newsletter'), + new OA\Property(property: 'description', type: 'string', example: 'Monthly updates'), + new OA\Property( + property: 'creation_date', + type: 'string', + format: 'date-time', + example: '2022-12-01T10:00:00Z' + ), + new OA\Property(property: 'public', type: 'boolean', example: true), + ], + type: 'object' + ) ), - new OA\Property(property: 'html_email', type: 'boolean', example: false), - new OA\Property(property: 'disabled', type: 'boolean', example: false), - new OA\Property(property: 'id', type: 'integer', example: 1) - ] + ], + type: 'object' ) ), new OA\Response( @@ -229,21 +238,19 @@ public function postAction(Request $request, SerializerInterface $serializer): J ) ] )] - public function getAction( - Request $request, - #[MapEntity(mapping: ['subscriberId' => 'id'])] Subscriber $subscriber, - ): JsonResponse { + public function getAction(Request $request, int $subscriberId, SerializerInterface $serializer): JsonResponse + { $this->requireAuthentication($request); - $subscriber = $this->subscriberRepository->findSubscriberWithSubscriptions($subscriber->getId()); + $subscriber = $this->subscriberRepository->findSubscriberWithSubscriptions($subscriberId); if (!$subscriber) { return new JsonResponse(['error' => 'Subscriber not found'], Response::HTTP_NOT_FOUND); } - $data = $this->subscriberNormalizer->normalize($subscriber); + $data = $serializer->serialize($subscriber, 'json'); - return new JsonResponse($data, Response::HTTP_OK, []); + return new JsonResponse($data, Response::HTTP_OK, [], true); } /** diff --git a/src/Serializer/SubscriberNormalizer.php b/src/Serializer/SubscriberNormalizer.php index fd0c32c..6e950bf 100644 --- a/src/Serializer/SubscriberNormalizer.php +++ b/src/Serializer/SubscriberNormalizer.php @@ -5,6 +5,7 @@ namespace PhpList\RestBundle\Serializer; use PhpList\Core\Domain\Model\Subscription\Subscriber; +use PhpList\Core\Domain\Model\Subscription\Subscription; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class SubscriberNormalizer implements NormalizerInterface @@ -18,11 +19,20 @@ public function normalize($object, string $format = null, array $context = []): return [ 'id' => $object->getId(), 'email' => $object->getEmail(), - 'subscribedLists' => array_map(function ($subscription) { - + 'creation_date' => $object->getCreationDate()->format('Y-m-d\TH:i:sP'), + 'confirmed' => $object->isConfirmed(), + 'blacklisted' => $object->isBlacklisted(), + 'bounce_count' => $object->getBounceCount(), + 'unique_id' => $object->getUniqueId(), + 'html_email' => $object->hasHtmlEmail(), + 'disabled' => $object->isDisabled(), + 'subscribedLists' => array_map(function (Subscription $subscription) { return [ 'id' => $subscription->getSubscriberList()->getId(), 'name' => $subscription->getSubscriberList()->getName(), + 'description' => $subscription->getSubscriberList()->getDescription(), + 'creation_date' => $subscription->getSubscriberList()->getCreationDate()->format('Y-m-d\TH:i:sP'), + 'public' => $subscription->getSubscriberList()->isPublic(), ]; }, $object->getSubscriptions()->toArray()), ]; diff --git a/tests/Integration/Controller/ListControllerTest.php b/tests/Integration/Controller/ListControllerTest.php index 516bc48..c66e61b 100644 --- a/tests/Integration/Controller/ListControllerTest.php +++ b/tests/Integration/Controller/ListControllerTest.php @@ -238,25 +238,50 @@ public function testGetListMembersWithCurrentSessionKeyForExistingListWithSubscr $this->assertJsonResponseContentEquals( [ [ - 'creation_date' => '2016-07-22T15:01:17+00:00', + 'id' => 1, 'email' => 'oliver@example.com', + 'creation_date' => '2016-07-22T15:01:17+00:00', 'confirmed' => true, 'blacklisted' => true, 'bounce_count' => 17, 'unique_id' => '95feb7fe7e06e6c11ca8d0c48cb46e89', 'html_email' => true, 'disabled' => true, - 'id' => 1, + 'subscribedLists' => [ + [ + 'id' => 2, + 'name' => 'More news', + 'description' => '', + 'creation_date' => '2016-06-22T15:01:17+00:00', + 'public' => true, + ], + ], ], [ - 'creation_date' => '2016-07-22T15:01:17+00:00', + 'id' => 2, 'email' => 'oliver1@example.com', + 'creation_date' => '2016-07-22T15:01:17+00:00', 'confirmed' => true, 'blacklisted' => true, 'bounce_count' => 17, 'unique_id' => '95feb7fe7e06e6c11ca8d0c48cb46e87', 'html_email' => true, 'disabled' => true, - 'id' => 2, + 'subscribedLists' => [ + [ + 'id' => 2, + 'name' => 'More news', + 'description' => '', + 'creation_date' => '2016-06-22T15:01:17+00:00', + 'public' => true, + ], + [ + 'id' => 1, + 'name' => 'News', + 'description' => 'News (and some fun stuff)', + 'creation_date' => '2016-06-22T15:01:17+00:00', + 'public' => true, + ], + ], ], ] ); From 61f2cb351eb65316120d893f8752e47aa0267da8 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 5 Mar 2025 22:29:07 +0400 Subject: [PATCH 11/13] ISSUE-344: fix warning --- src/Serializer/SubscriberNormalizer.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Serializer/SubscriberNormalizer.php b/src/Serializer/SubscriberNormalizer.php index 6e950bf..f572940 100644 --- a/src/Serializer/SubscriberNormalizer.php +++ b/src/Serializer/SubscriberNormalizer.php @@ -10,6 +10,9 @@ class SubscriberNormalizer implements NormalizerInterface { + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ public function normalize($object, string $format = null, array $context = []): array { if (!$object instanceof Subscriber) { @@ -38,6 +41,9 @@ public function normalize($object, string $format = null, array $context = []): ]; } + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ public function supportsNormalization($data, string $format = null): bool { return $data instanceof Subscriber; From fe193a4081d9c4302b169fefd75218bb8c3766a4 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 5 Mar 2025 22:38:44 +0400 Subject: [PATCH 12/13] ISSUE-344: fix style --- src/Controller/ListController.php | 6 +++++- src/Controller/SubscriberController.php | 6 +++++- src/Serializer/SubscriberNormalizer.php | 1 - tests/Integration/Controller/ListControllerTest.php | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Controller/ListController.php b/src/Controller/ListController.php index 6b05716..a09dfb1 100644 --- a/src/Controller/ListController.php +++ b/src/Controller/ListController.php @@ -321,7 +321,11 @@ public function deleteList( properties: [ new OA\Property(property: 'id', type: 'integer', example: 2), new OA\Property(property: 'name', type: 'string', example: 'Newsletter'), - new OA\Property(property: 'description', type: 'string', example: 'Monthly updates'), + new OA\Property( + property: 'description', + type: 'string', + example: 'Monthly updates' + ), new OA\Property( property: 'creation_date', type: 'string', diff --git a/src/Controller/SubscriberController.php b/src/Controller/SubscriberController.php index f35d76c..452ef42 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -203,7 +203,11 @@ public function postAction(Request $request, SerializerInterface $serializer): J properties: [ new OA\Property(property: 'id', type: 'integer', example: 2), new OA\Property(property: 'name', type: 'string', example: 'Newsletter'), - new OA\Property(property: 'description', type: 'string', example: 'Monthly updates'), + new OA\Property( + property: 'description', + type: 'string', + example: 'Monthly updates' + ), new OA\Property( property: 'creation_date', type: 'string', diff --git a/src/Serializer/SubscriberNormalizer.php b/src/Serializer/SubscriberNormalizer.php index f572940..99197a8 100644 --- a/src/Serializer/SubscriberNormalizer.php +++ b/src/Serializer/SubscriberNormalizer.php @@ -49,4 +49,3 @@ public function supportsNormalization($data, string $format = null): bool return $data instanceof Subscriber; } } - diff --git a/tests/Integration/Controller/ListControllerTest.php b/tests/Integration/Controller/ListControllerTest.php index c66e61b..5574f9c 100644 --- a/tests/Integration/Controller/ListControllerTest.php +++ b/tests/Integration/Controller/ListControllerTest.php @@ -247,7 +247,7 @@ public function testGetListMembersWithCurrentSessionKeyForExistingListWithSubscr 'unique_id' => '95feb7fe7e06e6c11ca8d0c48cb46e89', 'html_email' => true, 'disabled' => true, - 'subscribedLists' => [ + 'subscribedLists' => [ [ 'id' => 2, 'name' => 'More news', From b91fc09c990eef35384d56475f73154d9864436b Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 5 Mar 2025 22:54:57 +0400 Subject: [PATCH 13/13] ISSUE-344: core version update --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7d8ad82..095b5a6 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^8.1", - "phplist/core": "dev-ISSUE-344", + "phplist/core": "v5.0.0-alpha3", "friendsofsymfony/rest-bundle": "*", "symfony/test-pack": "^1.0", "symfony/process": "^6.4",