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. 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 diff --git a/composer.json b/composer.json index a613d5c..095b5a6 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^8.1", - "phplist/core": "dev-ISSUE-337", + "phplist/core": "v5.0.0-alpha3", "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/config/services.yml b/config/services.yml index 4aede00..36264ec 100644 --- a/config/services.yml +++ b/config/services.yml @@ -1,6 +1,6 @@ services: Psr\Container\ContainerInterface: - alias: 'service_container' + alias: 'service_container' PhpList\RestBundle\Controller\: resource: '../src/Controller' @@ -8,12 +8,12 @@ services: autowire: true tags: ['controller.service_arguments'] -# Symfony\Component\Serializer\SerializerInterface: -# autowire: true -# autoconfigure: true + # Symfony\Component\Serializer\SerializerInterface: + # 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 @@ -35,3 +35,6 @@ services: PhpList\RestBundle\EventListener\ResponseListener: tags: - { name: kernel.event_listener, event: kernel.response } + PhpList\RestBundle\Serializer\SubscriberNormalizer: + tags: [ 'serializer.normalizer' ] + autowire: true 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..a09dfb1 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,11 +270,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/{listId}/subscribers', name: 'get_subscriber_from_list', methods: ['GET'])] #[OA\Get( - path: '/lists/{list}/members', + path: '/lists/{listId}/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( @@ -282,7 +285,7 @@ public function deleteList( schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'list', + name: 'listId', description: 'List ID', in: 'path', required: true, @@ -297,24 +300,43 @@ 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' ) @@ -338,7 +360,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 +373,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/{list}/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 +388,7 @@ public function getListMembers( schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'list', + name: 'listId', description: 'List ID', in: 'path', required: true, @@ -396,7 +418,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 3d9087b..452ef42 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -29,17 +29,15 @@ class SubscriberController extends AbstractController private SubscriberRepository $subscriberRepository; - public function __construct( - Authentication $authentication, - SubscriberRepository $repository - ) { + public function __construct(Authentication $authentication, SubscriberRepository $repository) + { $this->authentication = $authentication; $this->subscriberRepository = $repository; } #[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 +47,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 +136,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); @@ -158,6 +154,109 @@ public function postAction(Request $request, SerializerInterface $serializer): J ); } + #[Route('/subscribers/{subscriberId}', name: 'get_subscriber_by_id', methods: ['GET'])] + #[OA\Get( + path: '/subscribers/{subscriberId}', + 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: 'subscriberId', + 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: '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: '2023-01-01T12:00:00Z' + ), + new OA\Property(property: 'confirmed', type: 'boolean', example: true), + 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: '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' + ) + ), + ], + type: 'object' + ) + ), + 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, int $subscriberId, SerializerInterface $serializer): JsonResponse + { + $this->requireAuthentication($request); + + $subscriber = $this->subscriberRepository->findSubscriberWithSubscriptions($subscriberId); + + if (!$subscriber) { + return new JsonResponse(['error' => 'Subscriber not found'], Response::HTTP_NOT_FOUND); + } + + $data = $serializer->serialize($subscriber, 'json'); + + return new JsonResponse($data, Response::HTTP_OK, [], true); + } + /** * @param Request $request * @@ -173,7 +272,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/src/Serializer/SubscriberNormalizer.php b/src/Serializer/SubscriberNormalizer.php new file mode 100644 index 0000000..99197a8 --- /dev/null +++ b/src/Serializer/SubscriberNormalizer.php @@ -0,0 +1,51 @@ + $object->getId(), + 'email' => $object->getEmail(), + '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()), + ]; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + 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 e12f4e2..5574f9c 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,30 +233,55 @@ 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( [ [ - '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, + ], + ], ], ] ); 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']); } }