diff --git a/config/mbin_routes/entry_api.yaml b/config/mbin_routes/entry_api.yaml index 890e67c0ed..c107a3147d 100644 --- a/config/mbin_routes/entry_api.yaml +++ b/config/mbin_routes/entry_api.yaml @@ -138,3 +138,17 @@ api_comment_favourite: path: /api/comments/{comment_id}/favourite methods: [ PUT ] format: json + +# boosts and upvotes for entries +api_entry_activity: + controller: App\Controller\Api\Entry\EntriesActivityApi + path: /api/entry/{entry_id}/activity + methods: [ GET ] + format: json + +# boosts and upvotes entry comments +api_entry_comment_activity: + controller: App\Controller\Api\Entry\Comments\EntryCommentsActivityApi + path: /api/comments/{comment_id}/activity + methods: [ GET ] + format: json diff --git a/config/mbin_routes/post_api.yaml b/config/mbin_routes/post_api.yaml index d97a71f448..b3f4bc0e58 100644 --- a/config/mbin_routes/post_api.yaml +++ b/config/mbin_routes/post_api.yaml @@ -134,3 +134,17 @@ api_post_comments_vote: path: /api/post-comments/{comment_id}/vote/{choice} methods: [ PUT ] format: json + +# boosts and upvotes for posts +api_post_activity: + controller: App\Controller\Api\Post\PostsActivityApi + path: /api/post/{post_id}/activity + methods: [ GET ] + format: json + +# boosts and upvotes entry post comments +api_post_comment_activity: + controller: App\Controller\Api\Post\Comments\PostCommentsActivityApi + path: /api/post-comments/{comment_id}/activity + methods: [ GET ] + format: json diff --git a/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php new file mode 100644 index 0000000000..e7933804f8 --- /dev/null +++ b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php @@ -0,0 +1,77 @@ +rateLimit($apiReadLimiter, $anonymousApiReadLimiter); + + $this->handlePrivateContent($comment); + + $dto = $dtoFactory->createActivitiesDto($comment); + + return new JsonResponse( + $dto, + headers: $headers + ); + } +} diff --git a/src/Controller/Api/Entry/EntriesActivityApi.php b/src/Controller/Api/Entry/EntriesActivityApi.php new file mode 100644 index 0000000000..3577efb5d1 --- /dev/null +++ b/src/Controller/Api/Entry/EntriesActivityApi.php @@ -0,0 +1,76 @@ +rateLimit($apiReadLimiter, $anonymousApiReadLimiter); + + $this->handlePrivateContent($entry); + + $dto = $dtoFactory->createActivitiesDto($entry); + + return new JsonResponse( + $dto, + headers: $headers + ); + } +} diff --git a/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php b/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php new file mode 100644 index 0000000000..a95a7efa15 --- /dev/null +++ b/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php @@ -0,0 +1,77 @@ +rateLimit($apiReadLimiter, $anonymousApiReadLimiter); + + $this->handlePrivateContent($comment); + + $dto = $dtoFactory->createActivitiesDto($comment); + + return new JsonResponse( + $dto, + headers: $headers + ); + } +} diff --git a/src/Controller/Api/Post/PostsActivityApi.php b/src/Controller/Api/Post/PostsActivityApi.php new file mode 100644 index 0000000000..eb97fdd6ba --- /dev/null +++ b/src/Controller/Api/Post/PostsActivityApi.php @@ -0,0 +1,76 @@ +rateLimit($apiReadLimiter, $anonymousApiReadLimiter); + + $this->handlePrivateContent($post); + + $dto = $dtoFactory->createActivitiesDto($post); + + return new JsonResponse( + $dto, + headers: $headers + ); + } +} diff --git a/src/DTO/ActivitiesResponseDto.php b/src/DTO/ActivitiesResponseDto.php new file mode 100644 index 0000000000..d7cac52386 --- /dev/null +++ b/src/DTO/ActivitiesResponseDto.php @@ -0,0 +1,45 @@ +boosts = $boosts; + $dto->upvotes = $upvotes; + $dto->downvotes = $downvotes; + + return $dto; + } + + public function jsonSerialize(): mixed + { + return [ + 'boosts' => $this->boosts, + 'upvotes' => $this->upvotes, + 'downvotes' => $this->downvotes, + ]; + } +} diff --git a/src/Factory/ContentActivityDtoFactory.php b/src/Factory/ContentActivityDtoFactory.php new file mode 100644 index 0000000000..2d8f5fe086 --- /dev/null +++ b/src/Factory/ContentActivityDtoFactory.php @@ -0,0 +1,38 @@ +getUpVotes() as $upvote) { + $dto->boosts[] = $this->userFactory->createSmallDto($upvote->user); + } + + if (property_exists($subject, 'favourites')) { + /* @var Favourite $favourite */ + foreach ($subject->favourites as $favourite) { + $dto->upvotes[] = $this->userFactory->createSmallDto($favourite->user); + } + } else { + $dto->upvotes = null; + } + + return $dto; + } +} diff --git a/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php b/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php new file mode 100644 index 0000000000..c75901d82e --- /dev/null +++ b/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php @@ -0,0 +1,90 @@ +getUserByUsername('user'); + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $entry = $this->getEntryByTitle('test article', body: 'test for activites', user: $user, magazine: $magazine); + $comment = $this->createEntryComment('test comment', $entry, $user); + + $this->client->jsonRequest('GET', "/api/comments/{$comment->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['boosts']); + self::assertSame([], $jsonData['upvotes']); + self::assertSame(null, $jsonData['downvotes']); + } + + public function testUpvotes() + { + $author = $this->getUserByUsername('userA'); + $user1 = $this->getUserByUsername('user1'); + $user2 = $this->getUserByUsername('user2'); + $this->getUserByUsername('user3'); + + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $entry = $this->getEntryByTitle('test article', body: 'test for activites', user: $author, magazine: $magazine); + $comment = $this->createEntryComment('test comment', $entry, $author); + + $this->favouriteManager->toggle($user1, $comment); + $this->favouriteManager->toggle($user2, $comment); + + $this->client->jsonRequest('GET', "/api/comments/{$comment->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['boosts']); + self::assertSame(null, $jsonData['downvotes']); + + self::assertCount(2, $jsonData['upvotes']); + self::assertTrue(array_all($jsonData['upvotes'], function ($u) use ($user1, $user2) { + /* @var UserSmallResponseDto $u */ + return $u['userId'] === $user1->getId() || $u['userId'] === $user2->getId(); + }), serialize($jsonData['upvotes'])); + } + + public function testBoosts() + { + $author = $this->getUserByUsername('userA'); + $user1 = $this->getUserByUsername('user1'); + $user2 = $this->getUserByUsername('user2'); + $this->getUserByUsername('user3'); + + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $entry = $this->getEntryByTitle('test article', body: 'test for activites', user: $author, magazine: $magazine); + $comment = $this->createEntryComment('test comment', $entry, $author); + + $this->voteManager->upvote($comment, $user1); + $this->voteManager->upvote($comment, $user2); + + $this->client->jsonRequest('GET', "/api/comments/{$comment->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['upvotes']); + self::assertSame(null, $jsonData['downvotes']); + + self::assertCount(2, $jsonData['boosts']); + self::assertTrue(array_all($jsonData['boosts'], function ($u) use ($user1, $user2) { + /* @var UserSmallResponseDto $u */ + return $u['userId'] === $user1->getId() || $u['userId'] === $user2->getId(); + }), serialize($jsonData['boosts'])); + } +} diff --git a/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php b/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php new file mode 100644 index 0000000000..522eb5270e --- /dev/null +++ b/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php @@ -0,0 +1,88 @@ +getUserByUsername('user'); + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $entry = $this->getEntryByTitle('test article', body: 'test for activites', user: $user, magazine: $magazine); + + $this->client->jsonRequest('GET', "/api/entry/{$entry->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(self::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['boosts']); + self::assertSame([], $jsonData['upvotes']); + self::assertSame(null, $jsonData['downvotes']); + } + + public function testUpvotes() + { + $author = $this->getUserByUsername('userA'); + $user1 = $this->getUserByUsername('user1'); + $user2 = $this->getUserByUsername('user2'); + $this->getUserByUsername('user3'); + + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $entry = $this->getEntryByTitle('test article', body: 'test for activites', user: $author, magazine: $magazine); + + $this->favouriteManager->toggle($user1, $entry); + $this->favouriteManager->toggle($user2, $entry); + + $this->client->jsonRequest('GET', "/api/entry/{$entry->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(self::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['boosts']); + self::assertSame(null, $jsonData['downvotes']); + + self::assertCount(2, $jsonData['upvotes']); + self::assertTrue(array_all($jsonData['upvotes'], function ($u) use ($user1, $user2) { + /* @var UserSmallResponseDto $u */ + return $u['userId'] === $user1->getId() || $u['userId'] === $user2->getId(); + }), serialize($jsonData['upvotes'])); + } + + public function testBoosts() + { + $author = $this->getUserByUsername('userA'); + $user1 = $this->getUserByUsername('user1'); + $user2 = $this->getUserByUsername('user2'); + $this->getUserByUsername('user3'); + + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $entry = $this->getEntryByTitle('test article', body: 'test for activites', user: $author, magazine: $magazine); + + $this->voteManager->upvote($entry, $user1); + $this->voteManager->upvote($entry, $user2); + + $this->client->jsonRequest('GET', "/api/entry/{$entry->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(self::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['upvotes']); + self::assertSame(null, $jsonData['downvotes']); + + self::assertCount(2, $jsonData['boosts']); + self::assertTrue(array_all($jsonData['boosts'], function ($u) use ($user1, $user2) { + /* @var UserSmallResponseDto $u */ + return $u['userId'] === $user1->getId() || $u['userId'] === $user2->getId(); + }), serialize($jsonData['boosts'])); + } +} diff --git a/tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php b/tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php new file mode 100644 index 0000000000..2bfa35e2a7 --- /dev/null +++ b/tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php @@ -0,0 +1,90 @@ +getUserByUsername('user'); + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $post = $this->createPost('test post', user: $user, magazine: $magazine); + $comment = $this->createPostComment('test comment', $post, $user); + + $this->client->jsonRequest('GET', "/api/post-comments/{$comment->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['boosts']); + self::assertSame([], $jsonData['upvotes']); + self::assertSame(null, $jsonData['downvotes']); + } + + public function testUpvotes() + { + $author = $this->getUserByUsername('userA'); + $user1 = $this->getUserByUsername('user1'); + $user2 = $this->getUserByUsername('user2'); + $this->getUserByUsername('user3'); + + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $post = $this->createPost('test post', user: $author, magazine: $magazine); + $comment = $this->createPostComment('test comment', $post, $author); + + $this->favouriteManager->toggle($user1, $comment); + $this->favouriteManager->toggle($user2, $comment); + + $this->client->jsonRequest('GET', "/api/post-comments/{$comment->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['boosts']); + self::assertSame(null, $jsonData['downvotes']); + + self::assertCount(2, $jsonData['upvotes']); + self::assertTrue(array_all($jsonData['upvotes'], function ($u) use ($user1, $user2) { + /* @var UserSmallResponseDto $u */ + return $u['userId'] === $user1->getId() || $u['userId'] === $user2->getId(); + }), serialize($jsonData['upvotes'])); + } + + public function testBoosts() + { + $author = $this->getUserByUsername('userA'); + $user1 = $this->getUserByUsername('user1'); + $user2 = $this->getUserByUsername('user2'); + $this->getUserByUsername('user3'); + + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $post = $this->createPost('test post', user: $author, magazine: $magazine); + $comment = $this->createPostComment('test comment', $post, $author); + + $this->voteManager->upvote($comment, $user1); + $this->voteManager->upvote($comment, $user2); + + $this->client->jsonRequest('GET', "/api/post-comments/{$comment->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['upvotes']); + self::assertSame(null, $jsonData['downvotes']); + + self::assertCount(2, $jsonData['boosts']); + self::assertTrue(array_all($jsonData['boosts'], function ($u) use ($user1, $user2) { + /* @var UserSmallResponseDto $u */ + return $u['userId'] === $user1->getId() || $u['userId'] === $user2->getId(); + }), serialize($jsonData['boosts'])); + } +} diff --git a/tests/Functional/Controller/Api/Post/PostsActivityApiTest.php b/tests/Functional/Controller/Api/Post/PostsActivityApiTest.php new file mode 100644 index 0000000000..5a87e8722f --- /dev/null +++ b/tests/Functional/Controller/Api/Post/PostsActivityApiTest.php @@ -0,0 +1,87 @@ +getUserByUsername('user'); + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $post = $this->createPost('test post', user: $user, magazine: $magazine); + + $this->client->jsonRequest('GET', "/api/post/{$post->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['boosts']); + self::assertSame([], $jsonData['upvotes']); + self::assertSame(null, $jsonData['downvotes']); + } + + public function testUpvotes() + { + $author = $this->getUserByUsername('userA'); + $user1 = $this->getUserByUsername('user1'); + $user2 = $this->getUserByUsername('user2'); + $this->getUserByUsername('user3'); + + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $post = $this->createPost('test post', user: $author, magazine: $magazine); + + $this->favouriteManager->toggle($user1, $post); + $this->favouriteManager->toggle($user2, $post); + + $this->client->jsonRequest('GET', "/api/post/{$post->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['boosts']); + self::assertSame(null, $jsonData['downvotes']); + + self::assertCount(2, $jsonData['upvotes']); + self::assertTrue(array_all($jsonData['upvotes'], function ($u) use ($user1, $user2) { + /* @var UserSmallResponseDto $u */ + return $u['userId'] === $user1->getId() || $u['userId'] === $user2->getId(); + }), serialize($jsonData['upvotes'])); + } + + public function testBoosts() + { + $author = $this->getUserByUsername('userA'); + $user1 = $this->getUserByUsername('user1'); + $user2 = $this->getUserByUsername('user2'); + $this->getUserByUsername('user3'); + + $magazine = $this->getMagazineByNameNoRSAKey('acme'); + $post = $this->createPost('test post', user: $author, magazine: $magazine); + + $this->voteManager->upvote($post, $user1); + $this->voteManager->upvote($post, $user2); + + $this->client->jsonRequest('GET', "/api/post/{$post->getId()}/activity"); + self::assertResponseIsSuccessful(); + $jsonData = self::getJsonResponse($this->client); + + self::assertIsArray($jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertSame([], $jsonData['upvotes']); + self::assertSame(null, $jsonData['downvotes']); + + self::assertCount(2, $jsonData['boosts']); + self::assertTrue(array_all($jsonData['boosts'], function ($u) use ($user1, $user2) { + /* @var UserSmallResponseDto $u */ + return $u['userId'] === $user1->getId() || $u['userId'] === $user2->getId(); + }), serialize($jsonData['boosts'])); + } +}