Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions config/mbin_routes/entry_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 14 additions & 0 deletions config/mbin_routes/post_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
77 changes: 77 additions & 0 deletions src/Controller/Api/Entry/Comments/EntryCommentsActivityApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

namespace App\Controller\Api\Entry\Comments;

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;

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,
ContentActivityDtoFactory $dtoFactory,
RateLimiterFactory $apiReadLimiter,
RateLimiterFactory $anonymousApiReadLimiter,
): JsonResponse {
$headers = $this->rateLimit($apiReadLimiter, $anonymousApiReadLimiter);

$this->handlePrivateContent($comment);

$dto = $dtoFactory->createActivitiesDto($comment);

return new JsonResponse(
$dto,
headers: $headers
);
}
}
76 changes: 76 additions & 0 deletions src/Controller/Api/Entry/EntriesActivityApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

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;

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,
ContentActivityDtoFactory $dtoFactory,
RateLimiterFactory $apiReadLimiter,
RateLimiterFactory $anonymousApiReadLimiter,
): JsonResponse {
$headers = $this->rateLimit($apiReadLimiter, $anonymousApiReadLimiter);

$this->handlePrivateContent($entry);

$dto = $dtoFactory->createActivitiesDto($entry);

return new JsonResponse(
$dto,
headers: $headers
);
}
}
77 changes: 77 additions & 0 deletions src/Controller/Api/Post/Comments/PostCommentsActivityApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

namespace App\Controller\Api\Post\Comments;

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;

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,
ContentActivityDtoFactory $dtoFactory,
RateLimiterFactory $apiReadLimiter,
RateLimiterFactory $anonymousApiReadLimiter,
): JsonResponse {
$headers = $this->rateLimit($apiReadLimiter, $anonymousApiReadLimiter);

$this->handlePrivateContent($comment);

$dto = $dtoFactory->createActivitiesDto($comment);

return new JsonResponse(
$dto,
headers: $headers
);
}
}
76 changes: 76 additions & 0 deletions src/Controller/Api/Post/PostsActivityApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

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;

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,
ContentActivityDtoFactory $dtoFactory,
RateLimiterFactory $apiReadLimiter,
RateLimiterFactory $anonymousApiReadLimiter,
): JsonResponse {
$headers = $this->rateLimit($apiReadLimiter, $anonymousApiReadLimiter);

$this->handlePrivateContent($post);

$dto = $dtoFactory->createActivitiesDto($post);

return new JsonResponse(
$dto,
headers: $headers
);
}
}
45 changes: 45 additions & 0 deletions src/DTO/ActivitiesResponseDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace App\DTO;

use OpenApi\Attributes as OA;

#[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')]
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 or it is not supported by the subject')]
public ?array $downvotes = null;

/**
* @param UserSmallResponseDto[]|null $boosts
* @param UserSmallResponseDto[]|null $upvotes
* @param UserSmallResponseDto[]|null $downvotes
*/
public static function create(
?array $boosts = null,
?array $upvotes = null,
?array $downvotes = null,
): ActivitiesResponseDto {
$dto = new self();
$dto->boosts = $boosts;
$dto->upvotes = $upvotes;
$dto->downvotes = $downvotes;

return $dto;
}

public function jsonSerialize(): mixed
{
return [
'boosts' => $this->boosts,
'upvotes' => $this->upvotes,
'downvotes' => $this->downvotes,
];
}
}
Loading
Loading