From e2a9d22270be8dc044c0a455d2f2d84569bc7caf Mon Sep 17 00:00:00 2001 From: blued_gear Date: Wed, 4 Feb 2026 23:36:39 +0000 Subject: [PATCH 1/6] add API endpoint to get boosts and upvotes of entry --- config/mbin_routes/entry_api.yaml | 7 ++ .../Api/Entry/EntriesActivityApi.php | 45 ++++++++++ src/DTO/ActivitiesResponseDto.php | 46 ++++++++++ .../Api/Entry/EntriesActivityApiTest.php | 84 +++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 src/Controller/Api/Entry/EntriesActivityApi.php create mode 100644 src/DTO/ActivitiesResponseDto.php create mode 100644 tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php diff --git a/config/mbin_routes/entry_api.yaml b/config/mbin_routes/entry_api.yaml index 890e67c0ed..173fb594af 100644 --- a/config/mbin_routes/entry_api.yaml +++ b/config/mbin_routes/entry_api.yaml @@ -138,3 +138,10 @@ api_comment_favourite: path: /api/comments/{comment_id}/favourite methods: [ PUT ] format: json + +# boosts and upvotes +api_entry_activity: + controller: App\Controller\Api\Entry\EntriesActivityApi + path: /api/entry/{entry_id}/activity + methods: [ GET ] + format: json diff --git a/src/Controller/Api/Entry/EntriesActivityApi.php b/src/Controller/Api/Entry/EntriesActivityApi.php new file mode 100644 index 0000000000..0f7d087670 --- /dev/null +++ b/src/Controller/Api/Entry/EntriesActivityApi.php @@ -0,0 +1,45 @@ +rateLimit($apiReadLimiter, $anonymousApiReadLimiter); + + $this->handlePrivateContent($entry); + + $dto = ActivitiesResponseDto::create([], [], null); + /* @var EntryVote $upvote */ + foreach ($entry->getUpVotes() as $upvote) { + $dto->boosts[] = $this->userFactory->createSmallDto($upvote->user); + } + /* @var EntryFavourite $favourite */ + foreach ($entry->favourites as $favourite) { + $dto->upvotes[] = $this->userFactory->createSmallDto($favourite->user); + } + + return new JsonResponse( + $dto, + headers: $headers + ); + } + +} diff --git a/src/DTO/ActivitiesResponseDto.php b/src/DTO/ActivitiesResponseDto.php new file mode 100644 index 0000000000..2b65bf57c7 --- /dev/null +++ b/src/DTO/ActivitiesResponseDto.php @@ -0,0 +1,46 @@ +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/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php b/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php new file mode 100644 index 0000000000..96bc2b3b07 --- /dev/null +++ b/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php @@ -0,0 +1,84 @@ +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'])); + } +} From 3ce62f2cb2c57ccdb173095e0a73e6f33360130c Mon Sep 17 00:00:00 2001 From: blued_gear Date: Thu, 5 Feb 2026 17:29:52 +0000 Subject: [PATCH 2/6] add description to the DTO attributes --- src/DTO/ActivitiesResponseDto.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DTO/ActivitiesResponseDto.php b/src/DTO/ActivitiesResponseDto.php index 2b65bf57c7..dd07534347 100644 --- a/src/DTO/ActivitiesResponseDto.php +++ b/src/DTO/ActivitiesResponseDto.php @@ -10,11 +10,11 @@ class ActivitiesResponseDto implements \JsonSerializable { - #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class))] + #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data')] public ?array $boosts = null; - #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class))] + #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data')] public ?array $upvotes = null; - #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class))] + #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data')] public ?array $downvotes = null; /** From f8faaab3ff2f95ce55be11e1ba80d925ca888f78 Mon Sep 17 00:00:00 2001 From: blued_gear Date: Thu, 5 Feb 2026 17:38:52 +0000 Subject: [PATCH 3/6] add API endpoint to get boosts and upvotes of entry comment --- config/mbin_routes/entry_api.yaml | 9 +- .../Comments/EntryCommentsActivityApi.php | 48 ++++++++++ .../Comment/EntryCommentsActivityApiTest.php | 87 +++++++++++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php create mode 100644 tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php diff --git a/config/mbin_routes/entry_api.yaml b/config/mbin_routes/entry_api.yaml index 173fb594af..c107a3147d 100644 --- a/config/mbin_routes/entry_api.yaml +++ b/config/mbin_routes/entry_api.yaml @@ -139,9 +139,16 @@ api_comment_favourite: methods: [ PUT ] format: json -# boosts and upvotes +# 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/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php new file mode 100644 index 0000000000..996ab766c9 --- /dev/null +++ b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php @@ -0,0 +1,48 @@ +rateLimit($apiReadLimiter, $anonymousApiReadLimiter); + + $this->handlePrivateContent($comment); + + $dto = ActivitiesResponseDto::create([], [], null); + /* @var EntryVote $upvote */ + foreach ($comment->getUpVotes() as $upvote) { + $dto->boosts[] = $this->userFactory->createSmallDto($upvote->user); + } + /* @var EntryFavourite $favourite */ + foreach ($comment->favourites as $favourite) { + $dto->upvotes[] = $this->userFactory->createSmallDto($favourite->user); + } + + return new JsonResponse( + $dto, + headers: $headers + ); + } + +} 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..d908f82117 --- /dev/null +++ b/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php @@ -0,0 +1,87 @@ +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(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); + $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(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); + $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(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'])); + } +} From a898bb9ade29e8d77aa9eee1fab242a8c287d699 Mon Sep 17 00:00:00 2001 From: blued_gear Date: Thu, 5 Feb 2026 18:06:19 +0000 Subject: [PATCH 4/6] refactor and implement the rest --- config/mbin_routes/post_api.yaml | 14 +++ .../Comments/EntryCommentsActivityApi.php | 15 ++-- .../Api/Entry/EntriesActivityApi.php | 13 +-- .../Post/Comments/PostCommentsActivityApi.php | 41 +++++++++ src/Controller/Api/Post/PostsActivityApi.php | 39 +++++++++ src/DTO/ActivitiesResponseDto.php | 6 +- src/Factory/ContentActivityDtoFactory.php | 38 ++++++++ .../Comment/EntryCommentsActivityApiTest.php | 9 +- .../Comment/PostCommentsActivityApiTest.php | 86 +++++++++++++++++++ .../Api/Post/PostsActivityApiTest.php | 83 ++++++++++++++++++ 10 files changed, 316 insertions(+), 28 deletions(-) create mode 100644 src/Controller/Api/Post/Comments/PostCommentsActivityApi.php create mode 100644 src/Controller/Api/Post/PostsActivityApi.php create mode 100644 src/Factory/ContentActivityDtoFactory.php create mode 100644 tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php create mode 100644 tests/Functional/Controller/Api/Post/PostsActivityApiTest.php 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 index 996ab766c9..ab9f54a423 100644 --- a/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php +++ b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php @@ -7,8 +7,11 @@ use App\DTO\ActivitiesResponseDto; use App\Entity\Entry; use App\Entity\EntryComment; +use App\Entity\EntryCommentFavourite; +use App\Entity\EntryCommentVote; use App\Entity\EntryFavourite; use App\Entity\EntryVote; +use App\Factory\ContentActivityDtoFactory; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\RateLimiter\RateLimiterFactory; @@ -22,6 +25,7 @@ class EntryCommentsActivityApi extends EntriesBaseApi public function __invoke( #[MapEntity(id: 'comment_id')] EntryComment $comment, + ContentActivityDtoFactory $dtoFactory, RateLimiterFactory $apiReadLimiter, RateLimiterFactory $anonymousApiReadLimiter, ): JsonResponse { @@ -29,16 +33,7 @@ public function __invoke( $this->handlePrivateContent($comment); - $dto = ActivitiesResponseDto::create([], [], null); - /* @var EntryVote $upvote */ - foreach ($comment->getUpVotes() as $upvote) { - $dto->boosts[] = $this->userFactory->createSmallDto($upvote->user); - } - /* @var EntryFavourite $favourite */ - foreach ($comment->favourites as $favourite) { - $dto->upvotes[] = $this->userFactory->createSmallDto($favourite->user); - } - + $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 index 0f7d087670..fcec8d384f 100644 --- a/src/Controller/Api/Entry/EntriesActivityApi.php +++ b/src/Controller/Api/Entry/EntriesActivityApi.php @@ -7,6 +7,7 @@ use App\Entity\Entry; use App\Entity\EntryFavourite; use App\Entity\EntryVote; +use App\Factory\ContentActivityDtoFactory; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\RateLimiter\RateLimiterFactory; @@ -19,6 +20,7 @@ class EntriesActivityApi extends EntriesBaseApi public function __invoke( #[MapEntity(id: 'entry_id')] Entry $entry, + ContentActivityDtoFactory $dtoFactory, RateLimiterFactory $apiReadLimiter, RateLimiterFactory $anonymousApiReadLimiter, ): JsonResponse { @@ -26,16 +28,7 @@ public function __invoke( $this->handlePrivateContent($entry); - $dto = ActivitiesResponseDto::create([], [], null); - /* @var EntryVote $upvote */ - foreach ($entry->getUpVotes() as $upvote) { - $dto->boosts[] = $this->userFactory->createSmallDto($upvote->user); - } - /* @var EntryFavourite $favourite */ - foreach ($entry->favourites as $favourite) { - $dto->upvotes[] = $this->userFactory->createSmallDto($favourite->user); - } - + $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..b5f3ce0376 --- /dev/null +++ b/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php @@ -0,0 +1,41 @@ +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..698ca5fdc9 --- /dev/null +++ b/src/Controller/Api/Post/PostsActivityApi.php @@ -0,0 +1,39 @@ +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 index dd07534347..7b6575d6dd 100644 --- a/src/DTO/ActivitiesResponseDto.php +++ b/src/DTO/ActivitiesResponseDto.php @@ -10,11 +10,11 @@ class ActivitiesResponseDto implements \JsonSerializable { - #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data')] + #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data or it is not supported by the subject')] public ?array $boosts = null; - #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data')] + #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data or it is not supported by the subject')] public ?array $upvotes = null; - #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data')] + #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data or it is not supported by the subject')] public ?array $downvotes = null; /** diff --git a/src/Factory/ContentActivityDtoFactory.php b/src/Factory/ContentActivityDtoFactory.php new file mode 100644 index 0000000000..813e853280 --- /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 index d908f82117..015f2e1a25 100644 --- a/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php +++ b/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php @@ -3,13 +3,12 @@ namespace App\Tests\Functional\Controller\Api\Entry\Comment; use App\DTO\UserSmallResponseDto; +use App\Tests\Functional\Controller\Api\Entry\EntriesActivityApiTest; use App\Tests\WebTestCase; class EntryCommentsActivityApiTest extends WebTestCase { - public const array ACTIVITIES_RESPONSE_DTO_KEYS = ['boosts', 'upvotes', 'downvotes']; - public function testEmpty() { $user = $this->getUserByUsername('user'); $magazine = $this->getMagazineByNameNoRSAKey('acme'); @@ -21,7 +20,7 @@ public function testEmpty() { $jsonData = self::getJsonResponse($this->client); self::assertIsArray($jsonData); - self::assertArrayKeysMatch(self::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); self::assertSame([], $jsonData['boosts']); self::assertSame([], $jsonData['upvotes']); self::assertSame(null, $jsonData['downvotes']); @@ -45,7 +44,7 @@ public function testUpvotes() { $jsonData = self::getJsonResponse($this->client); self::assertIsArray($jsonData); - self::assertArrayKeysMatch(self::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); self::assertSame([], $jsonData['boosts']); self::assertSame(null, $jsonData['downvotes']); @@ -74,7 +73,7 @@ public function testBoosts() { $jsonData = self::getJsonResponse($this->client); self::assertIsArray($jsonData); - self::assertArrayKeysMatch(self::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); + self::assertArrayKeysMatch(EntriesActivityApiTest::ACTIVITIES_RESPONSE_DTO_KEYS, $jsonData); self::assertSame([], $jsonData['upvotes']); self::assertSame(null, $jsonData['downvotes']); 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..b822258138 --- /dev/null +++ b/tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php @@ -0,0 +1,86 @@ +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..4964937a22 --- /dev/null +++ b/tests/Functional/Controller/Api/Post/PostsActivityApiTest.php @@ -0,0 +1,83 @@ +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'])); + } +} From 87567ca0dbad95c0be124892bbe60aeb5ebb416c Mon Sep 17 00:00:00 2001 From: blued_gear Date: Thu, 5 Feb 2026 18:07:46 +0000 Subject: [PATCH 5/6] linter --- .../Comments/EntryCommentsActivityApi.php | 12 +++-------- .../Api/Entry/EntriesActivityApi.php | 8 +++----- .../Post/Comments/PostCommentsActivityApi.php | 16 ++++++--------- src/Controller/Api/Post/PostsActivityApi.php | 15 ++++++-------- src/DTO/ActivitiesResponseDto.php | 3 +-- src/Factory/ContentActivityDtoFactory.php | 14 ++++++------- .../Comment/EntryCommentsActivityApiTest.php | 20 +++++++++++-------- .../Api/Entry/EntriesActivityApiTest.php | 20 +++++++++++-------- .../Comment/PostCommentsActivityApiTest.php | 20 +++++++++++-------- .../Api/Post/PostsActivityApiTest.php | 20 +++++++++++-------- 10 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php index ab9f54a423..2490da9b42 100644 --- a/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php +++ b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php @@ -1,25 +1,19 @@ 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 index fcec8d384f..544b07d0bb 100644 --- a/src/Controller/Api/Entry/EntriesActivityApi.php +++ b/src/Controller/Api/Entry/EntriesActivityApi.php @@ -1,12 +1,11 @@ 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 index b5f3ce0376..025baa8581 100644 --- a/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php +++ b/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php @@ -1,16 +1,12 @@ 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 index 698ca5fdc9..304fb102d5 100644 --- a/src/Controller/Api/Post/PostsActivityApi.php +++ b/src/Controller/Api/Post/PostsActivityApi.php @@ -1,14 +1,11 @@ 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 index 7b6575d6dd..d7cac52386 100644 --- a/src/DTO/ActivitiesResponseDto.php +++ b/src/DTO/ActivitiesResponseDto.php @@ -9,7 +9,6 @@ #[OA\Schema] class ActivitiesResponseDto implements \JsonSerializable { - #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data or it is not supported by the subject')] public ?array $boosts = null; #[OA\Property(type: 'array', nullable: true, items: new OA\Items(type: UserSmallResponseDto::class), description: 'null if the user is not allowed to access the data or it is not supported by the subject')] @@ -21,7 +20,6 @@ class ActivitiesResponseDto implements \JsonSerializable * @param UserSmallResponseDto[]|null $boosts * @param UserSmallResponseDto[]|null $upvotes * @param UserSmallResponseDto[]|null $downvotes - * @return ActivitiesResponseDto */ public static function create( ?array $boosts = null, @@ -32,6 +30,7 @@ public static function create( $dto->boosts = $boosts; $dto->upvotes = $upvotes; $dto->downvotes = $downvotes; + return $dto; } diff --git a/src/Factory/ContentActivityDtoFactory.php b/src/Factory/ContentActivityDtoFactory.php index 813e853280..2d8f5fe086 100644 --- a/src/Factory/ContentActivityDtoFactory.php +++ b/src/Factory/ContentActivityDtoFactory.php @@ -1,29 +1,30 @@ getUpVotes() as $upvote) { $dto->boosts[] = $this->userFactory->createSmallDto($upvote->user); } - if(\property_exists($subject, 'favourites')) { + if (property_exists($subject, 'favourites')) { /* @var Favourite $favourite */ foreach ($subject->favourites as $favourite) { $dto->upvotes[] = $this->userFactory->createSmallDto($favourite->user); @@ -34,5 +35,4 @@ public function createActivitiesDto(VotableInterface $subject): ActivitiesRespon return $dto; } - } diff --git a/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php b/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php index 015f2e1a25..c75901d82e 100644 --- a/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php +++ b/tests/Functional/Controller/Api/Entry/Comment/EntryCommentsActivityApiTest.php @@ -1,5 +1,7 @@ getUserByUsername('user'); $magazine = $this->getMagazineByNameNoRSAKey('acme'); $entry = $this->getEntryByTitle('test article', body: 'test for activites', user: $user, magazine: $magazine); @@ -26,7 +28,8 @@ public function testEmpty() { self::assertSame(null, $jsonData['downvotes']); } - public function testUpvotes() { + public function testUpvotes() + { $author = $this->getUserByUsername('userA'); $user1 = $this->getUserByUsername('user1'); $user2 = $this->getUserByUsername('user2'); @@ -49,13 +52,14 @@ public function testUpvotes() { self::assertSame(null, $jsonData['downvotes']); self::assertCount(2, $jsonData['upvotes']); - self::assertTrue(\array_all($jsonData['upvotes'], function ($u) use ($user1, $user2) { + 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'])); + }), serialize($jsonData['upvotes'])); } - public function testBoosts() { + public function testBoosts() + { $author = $this->getUserByUsername('userA'); $user1 = $this->getUserByUsername('user1'); $user2 = $this->getUserByUsername('user2'); @@ -78,9 +82,9 @@ public function testBoosts() { self::assertSame(null, $jsonData['downvotes']); self::assertCount(2, $jsonData['boosts']); - self::assertTrue(\array_all($jsonData['boosts'], function ($u) use ($user1, $user2) { + 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'])); + }), serialize($jsonData['boosts'])); } } diff --git a/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php b/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php index 96bc2b3b07..522eb5270e 100644 --- a/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php +++ b/tests/Functional/Controller/Api/Entry/EntriesActivityApiTest.php @@ -1,5 +1,7 @@ getUserByUsername('user'); $magazine = $this->getMagazineByNameNoRSAKey('acme'); $entry = $this->getEntryByTitle('test article', body: 'test for activites', user: $user, magazine: $magazine); @@ -26,7 +28,8 @@ public function testEmpty() { self::assertSame(null, $jsonData['downvotes']); } - public function testUpvotes() { + public function testUpvotes() + { $author = $this->getUserByUsername('userA'); $user1 = $this->getUserByUsername('user1'); $user2 = $this->getUserByUsername('user2'); @@ -48,13 +51,14 @@ public function testUpvotes() { self::assertSame(null, $jsonData['downvotes']); self::assertCount(2, $jsonData['upvotes']); - self::assertTrue(\array_all($jsonData['upvotes'], function ($u) use ($user1, $user2) { + 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'])); + }), serialize($jsonData['upvotes'])); } - public function testBoosts() { + public function testBoosts() + { $author = $this->getUserByUsername('userA'); $user1 = $this->getUserByUsername('user1'); $user2 = $this->getUserByUsername('user2'); @@ -76,9 +80,9 @@ public function testBoosts() { self::assertSame(null, $jsonData['downvotes']); self::assertCount(2, $jsonData['boosts']); - self::assertTrue(\array_all($jsonData['boosts'], function ($u) use ($user1, $user2) { + 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'])); + }), serialize($jsonData['boosts'])); } } diff --git a/tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php b/tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php index b822258138..2bfa35e2a7 100644 --- a/tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php +++ b/tests/Functional/Controller/Api/Post/Comment/PostCommentsActivityApiTest.php @@ -1,5 +1,7 @@ getUserByUsername('user'); $magazine = $this->getMagazineByNameNoRSAKey('acme'); $post = $this->createPost('test post', user: $user, magazine: $magazine); @@ -26,7 +28,8 @@ public function testEmpty() { self::assertSame(null, $jsonData['downvotes']); } - public function testUpvotes() { + public function testUpvotes() + { $author = $this->getUserByUsername('userA'); $user1 = $this->getUserByUsername('user1'); $user2 = $this->getUserByUsername('user2'); @@ -49,13 +52,14 @@ public function testUpvotes() { self::assertSame(null, $jsonData['downvotes']); self::assertCount(2, $jsonData['upvotes']); - self::assertTrue(\array_all($jsonData['upvotes'], function ($u) use ($user1, $user2) { + 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'])); + }), serialize($jsonData['upvotes'])); } - public function testBoosts() { + public function testBoosts() + { $author = $this->getUserByUsername('userA'); $user1 = $this->getUserByUsername('user1'); $user2 = $this->getUserByUsername('user2'); @@ -78,9 +82,9 @@ public function testBoosts() { self::assertSame(null, $jsonData['downvotes']); self::assertCount(2, $jsonData['boosts']); - self::assertTrue(\array_all($jsonData['boosts'], function ($u) use ($user1, $user2) { + 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'])); + }), serialize($jsonData['boosts'])); } } diff --git a/tests/Functional/Controller/Api/Post/PostsActivityApiTest.php b/tests/Functional/Controller/Api/Post/PostsActivityApiTest.php index 4964937a22..5a87e8722f 100644 --- a/tests/Functional/Controller/Api/Post/PostsActivityApiTest.php +++ b/tests/Functional/Controller/Api/Post/PostsActivityApiTest.php @@ -1,5 +1,7 @@ getUserByUsername('user'); $magazine = $this->getMagazineByNameNoRSAKey('acme'); $post = $this->createPost('test post', user: $user, magazine: $magazine); @@ -25,7 +27,8 @@ public function testEmpty() { self::assertSame(null, $jsonData['downvotes']); } - public function testUpvotes() { + public function testUpvotes() + { $author = $this->getUserByUsername('userA'); $user1 = $this->getUserByUsername('user1'); $user2 = $this->getUserByUsername('user2'); @@ -47,13 +50,14 @@ public function testUpvotes() { self::assertSame(null, $jsonData['downvotes']); self::assertCount(2, $jsonData['upvotes']); - self::assertTrue(\array_all($jsonData['upvotes'], function ($u) use ($user1, $user2) { + 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'])); + }), serialize($jsonData['upvotes'])); } - public function testBoosts() { + public function testBoosts() + { $author = $this->getUserByUsername('userA'); $user1 = $this->getUserByUsername('user1'); $user2 = $this->getUserByUsername('user2'); @@ -75,9 +79,9 @@ public function testBoosts() { self::assertSame(null, $jsonData['downvotes']); self::assertCount(2, $jsonData['boosts']); - self::assertTrue(\array_all($jsonData['boosts'], function ($u) use ($user1, $user2) { + 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'])); + }), serialize($jsonData['boosts'])); } } From 66280f3e84dd4d8baabc631dbd3b8ad412aa10aa Mon Sep 17 00:00:00 2001 From: blued_gear Date: Thu, 5 Feb 2026 20:22:08 +0000 Subject: [PATCH 6/6] add API doc --- .../Comments/EntryCommentsActivityApi.php | 40 +++++++++++++++++++ .../Api/Entry/EntriesActivityApi.php | 40 +++++++++++++++++++ .../Post/Comments/PostCommentsActivityApi.php | 40 +++++++++++++++++++ src/Controller/Api/Post/PostsActivityApi.php | 40 +++++++++++++++++++ 4 files changed, 160 insertions(+) diff --git a/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php index 2490da9b42..e7933804f8 100644 --- a/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php +++ b/src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php @@ -6,8 +6,11 @@ use App\Controller\Api\Entry\EntriesBaseApi; use App\Controller\Traits\PrivateContentTrait; +use App\DTO\ActivitiesResponseDto; use App\Entity\EntryComment; use App\Factory\ContentActivityDtoFactory; +use Nelmio\ApiDocBundle\Attribute\Model; +use OpenApi\Attributes as OA; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\RateLimiter\RateLimiterFactory; @@ -16,6 +19,43 @@ class EntryCommentsActivityApi extends EntriesBaseApi { use PrivateContentTrait; + #[OA\Response( + response: 200, + description: 'Vote activity of the comment', + content: new Model(type: ActivitiesResponseDto::class), + headers: [ + new OA\Header(header: 'X-RateLimit-Remaining', schema: new OA\Schema(type: 'integer'), description: 'Number of requests left until you will be rate limited'), + new OA\Header(header: 'X-RateLimit-Retry-After', schema: new OA\Schema(type: 'integer'), description: 'Unix timestamp to retry the request after'), + new OA\Header(header: 'X-RateLimit-Limit', schema: new OA\Schema(type: 'integer'), description: 'Number of requests available'), + ] + )] + #[OA\Response( + response: 403, + description: 'You are not authorized to access this comment', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\ForbiddenErrorSchema::class)) + )] + #[OA\Response( + response: 404, + description: 'Comment not found', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\NotFoundErrorSchema::class)) + )] + #[OA\Response( + response: 429, + description: 'You are being rate limited', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\TooManyRequestsErrorSchema::class)), + headers: [ + new OA\Header(header: 'X-RateLimit-Remaining', schema: new OA\Schema(type: 'integer'), description: 'Number of requests left until you will be rate limited'), + new OA\Header(header: 'X-RateLimit-Retry-After', schema: new OA\Schema(type: 'integer'), description: 'Unix timestamp to retry the request after'), + new OA\Header(header: 'X-RateLimit-Limit', schema: new OA\Schema(type: 'integer'), description: 'Number of requests available'), + ] + )] + #[OA\Parameter( + name: 'comment_id', + in: 'path', + description: 'The comment to query', + schema: new OA\Schema(type: 'integer'), + )] + #[OA\Tag(name: 'entry_comment')] public function __invoke( #[MapEntity(id: 'comment_id')] EntryComment $comment, diff --git a/src/Controller/Api/Entry/EntriesActivityApi.php b/src/Controller/Api/Entry/EntriesActivityApi.php index 544b07d0bb..3577efb5d1 100644 --- a/src/Controller/Api/Entry/EntriesActivityApi.php +++ b/src/Controller/Api/Entry/EntriesActivityApi.php @@ -5,8 +5,11 @@ namespace App\Controller\Api\Entry; use App\Controller\Traits\PrivateContentTrait; +use App\DTO\ActivitiesResponseDto; use App\Entity\Entry; use App\Factory\ContentActivityDtoFactory; +use Nelmio\ApiDocBundle\Attribute\Model; +use OpenApi\Attributes as OA; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\RateLimiter\RateLimiterFactory; @@ -15,6 +18,43 @@ class EntriesActivityApi extends EntriesBaseApi { use PrivateContentTrait; + #[OA\Response( + response: 200, + description: 'Vote activity of the entry', + content: new Model(type: ActivitiesResponseDto::class), + headers: [ + new OA\Header(header: 'X-RateLimit-Remaining', schema: new OA\Schema(type: 'integer'), description: 'Number of requests left until you will be rate limited'), + new OA\Header(header: 'X-RateLimit-Retry-After', schema: new OA\Schema(type: 'integer'), description: 'Unix timestamp to retry the request after'), + new OA\Header(header: 'X-RateLimit-Limit', schema: new OA\Schema(type: 'integer'), description: 'Number of requests available'), + ] + )] + #[OA\Response( + response: 403, + description: 'You are not authorized to access this entry', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\ForbiddenErrorSchema::class)) + )] + #[OA\Response( + response: 404, + description: 'Entry not found', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\NotFoundErrorSchema::class)) + )] + #[OA\Response( + response: 429, + description: 'You are being rate limited', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\TooManyRequestsErrorSchema::class)), + headers: [ + new OA\Header(header: 'X-RateLimit-Remaining', schema: new OA\Schema(type: 'integer'), description: 'Number of requests left until you will be rate limited'), + new OA\Header(header: 'X-RateLimit-Retry-After', schema: new OA\Schema(type: 'integer'), description: 'Unix timestamp to retry the request after'), + new OA\Header(header: 'X-RateLimit-Limit', schema: new OA\Schema(type: 'integer'), description: 'Number of requests available'), + ] + )] + #[OA\Parameter( + name: 'entry_id', + in: 'path', + description: 'The entry to query', + schema: new OA\Schema(type: 'integer'), + )] + #[OA\Tag(name: 'entry')] public function __invoke( #[MapEntity(id: 'entry_id')] Entry $entry, diff --git a/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php b/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php index 025baa8581..a95a7efa15 100644 --- a/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php +++ b/src/Controller/Api/Post/Comments/PostCommentsActivityApi.php @@ -6,8 +6,11 @@ use App\Controller\Api\Post\PostsBaseApi; use App\Controller\Traits\PrivateContentTrait; +use App\DTO\ActivitiesResponseDto; use App\Entity\PostComment; use App\Factory\ContentActivityDtoFactory; +use Nelmio\ApiDocBundle\Attribute\Model; +use OpenApi\Attributes as OA; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\RateLimiter\RateLimiterFactory; @@ -16,6 +19,43 @@ class PostCommentsActivityApi extends PostsBaseApi { use PrivateContentTrait; + #[OA\Response( + response: 200, + description: 'Vote activity of the comment', + content: new Model(type: ActivitiesResponseDto::class), + headers: [ + new OA\Header(header: 'X-RateLimit-Remaining', schema: new OA\Schema(type: 'integer'), description: 'Number of requests left until you will be rate limited'), + new OA\Header(header: 'X-RateLimit-Retry-After', schema: new OA\Schema(type: 'integer'), description: 'Unix timestamp to retry the request after'), + new OA\Header(header: 'X-RateLimit-Limit', schema: new OA\Schema(type: 'integer'), description: 'Number of requests available'), + ] + )] + #[OA\Response( + response: 403, + description: 'You are not authorized to access this comment', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\ForbiddenErrorSchema::class)) + )] + #[OA\Response( + response: 404, + description: 'Comment not found', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\NotFoundErrorSchema::class)) + )] + #[OA\Response( + response: 429, + description: 'You are being rate limited', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\TooManyRequestsErrorSchema::class)), + headers: [ + new OA\Header(header: 'X-RateLimit-Remaining', schema: new OA\Schema(type: 'integer'), description: 'Number of requests left until you will be rate limited'), + new OA\Header(header: 'X-RateLimit-Retry-After', schema: new OA\Schema(type: 'integer'), description: 'Unix timestamp to retry the request after'), + new OA\Header(header: 'X-RateLimit-Limit', schema: new OA\Schema(type: 'integer'), description: 'Number of requests available'), + ] + )] + #[OA\Parameter( + name: 'comment_id', + in: 'path', + description: 'The comment to query', + schema: new OA\Schema(type: 'integer'), + )] + #[OA\Tag(name: 'post_comment')] public function __invoke( #[MapEntity(id: 'comment_id')] PostComment $comment, diff --git a/src/Controller/Api/Post/PostsActivityApi.php b/src/Controller/Api/Post/PostsActivityApi.php index 304fb102d5..eb97fdd6ba 100644 --- a/src/Controller/Api/Post/PostsActivityApi.php +++ b/src/Controller/Api/Post/PostsActivityApi.php @@ -5,8 +5,11 @@ namespace App\Controller\Api\Post; use App\Controller\Traits\PrivateContentTrait; +use App\DTO\ActivitiesResponseDto; use App\Entity\Post; use App\Factory\ContentActivityDtoFactory; +use Nelmio\ApiDocBundle\Attribute\Model; +use OpenApi\Attributes as OA; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\RateLimiter\RateLimiterFactory; @@ -15,6 +18,43 @@ class PostsActivityApi extends PostsBaseApi { use PrivateContentTrait; + #[OA\Response( + response: 200, + description: 'Vote activity of the post', + content: new Model(type: ActivitiesResponseDto::class), + headers: [ + new OA\Header(header: 'X-RateLimit-Remaining', schema: new OA\Schema(type: 'integer'), description: 'Number of requests left until you will be rate limited'), + new OA\Header(header: 'X-RateLimit-Retry-After', schema: new OA\Schema(type: 'integer'), description: 'Unix timestamp to retry the request after'), + new OA\Header(header: 'X-RateLimit-Limit', schema: new OA\Schema(type: 'integer'), description: 'Number of requests available'), + ] + )] + #[OA\Response( + response: 403, + description: 'You are not authorized to access this post', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\ForbiddenErrorSchema::class)) + )] + #[OA\Response( + response: 404, + description: 'Post not found', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\NotFoundErrorSchema::class)) + )] + #[OA\Response( + response: 429, + description: 'You are being rate limited', + content: new OA\JsonContent(ref: new Model(type: \App\Schema\Errors\TooManyRequestsErrorSchema::class)), + headers: [ + new OA\Header(header: 'X-RateLimit-Remaining', schema: new OA\Schema(type: 'integer'), description: 'Number of requests left until you will be rate limited'), + new OA\Header(header: 'X-RateLimit-Retry-After', schema: new OA\Schema(type: 'integer'), description: 'Unix timestamp to retry the request after'), + new OA\Header(header: 'X-RateLimit-Limit', schema: new OA\Schema(type: 'integer'), description: 'Number of requests available'), + ] + )] + #[OA\Parameter( + name: 'post_id', + in: 'path', + description: 'The post to query', + schema: new OA\Schema(type: 'integer'), + )] + #[OA\Tag(name: 'post')] public function __invoke( #[MapEntity(id: 'post_id')] Post $post,