diff --git a/app/Docs/Attributes/Limit.php b/app/Docs/Attributes/Limit.php new file mode 100644 index 00000000000..62c42906fbe --- /dev/null +++ b/app/Docs/Attributes/Limit.php @@ -0,0 +1,29 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace App\Docs\Attributes; + +use App\Models\Model; +use Attribute; +use Knuckles\Scribe\Attributes\GenericParam; + +#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)] +class Limit extends GenericParam +{ + public function __construct( + int $default = Model::PER_PAGE, + int $minimum = 1, + int $maximum = 50, + string $description = 'Maximum number of results', + bool $required = false, + string|int $example = 'No-example', + ) { + $description .= " (Default: {$default}, Minimum: {$minimum}, Maximum: {$maximum})"; + + parent::__construct('limit', 'integer', $description, $required, $example); + } +} diff --git a/app/Docs/Attributes/Offset.php b/app/Docs/Attributes/Offset.php new file mode 100644 index 00000000000..2fd162ead95 --- /dev/null +++ b/app/Docs/Attributes/Offset.php @@ -0,0 +1,23 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace App\Docs\Attributes; + +use Attribute; +use Knuckles\Scribe\Attributes\GenericParam; + +#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)] +class Offset extends GenericParam +{ + public function __construct( + string $description = 'Result offset for pagination.', + bool $required = false, + ?int $example = 1, + ) { + parent::__construct('offset', 'integer', $description, $required, $example ?? 'No-example'); + } +} diff --git a/app/Docs/Attributes/Page.php b/app/Docs/Attributes/Page.php new file mode 100644 index 00000000000..64f9b869044 --- /dev/null +++ b/app/Docs/Attributes/Page.php @@ -0,0 +1,23 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace App\Docs\Attributes; + +use Attribute; +use Knuckles\Scribe\Attributes\GenericParam; + +#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)] +class Page extends GenericParam +{ + public function __construct( + string $description = 'Search result page', + bool $required = false, + ?int $example = 1, + ) { + parent::__construct('page', 'integer', $description, $required, $example ?? 'No-example'); + } +} diff --git a/app/Docs/Attributes/Sort.php b/app/Docs/Attributes/Sort.php new file mode 100644 index 00000000000..c925922a278 --- /dev/null +++ b/app/Docs/Attributes/Sort.php @@ -0,0 +1,34 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace App\Docs\Attributes; + +use Attribute; +use Knuckles\Scribe\Attributes\GenericParam; + +#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)] +class Sort extends GenericParam +{ + public function __construct( + array|string $sorts, + ?string $defaultText = null, + ?string $example = null, + ) { + if (is_string($sorts)) { + $hash = mb_strtolower($sorts); + $description = "Sort option as defined in [{$sorts}](#{$hash})."; + } else { + $description = 'Sort order; '.implode(', ', array_map(fn ($sort) => "`{$sort}`", $sorts)); + } + + if ($defaultText !== null) { + $description .= " {$defaultText}"; + } + + parent::__construct('sort', 'string', $description, false, $example ?? 'No-example'); + } +} diff --git a/app/Docs/Strategies/GetFromQueryParamAttribute.php b/app/Docs/Strategies/GetFromQueryParamAttribute.php new file mode 100644 index 00000000000..83d7fb3e73e --- /dev/null +++ b/app/Docs/Strategies/GetFromQueryParamAttribute.php @@ -0,0 +1,26 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace App\Docs\Strategies; + +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Offset; +use App\Docs\Attributes\Page; +use App\Docs\Attributes\Sort; +use Http\Message\Authentication\QueryParam; +use Knuckles\Scribe\Extracting\Strategies\GetParamsFromAttributeStrategy; + +class GetFromQueryParamAttribute extends GetParamsFromAttributeStrategy +{ + protected static array $attributeNames = [ + Limit::class, + Offset::class, + Page::class, + QueryParam::class, + Sort::class, + ]; +} diff --git a/app/Http/Controllers/BeatmapDiscussionPostsController.php b/app/Http/Controllers/BeatmapDiscussionPostsController.php index f02f81f125c..541b9e8c70d 100644 --- a/app/Http/Controllers/BeatmapDiscussionPostsController.php +++ b/app/Http/Controllers/BeatmapDiscussionPostsController.php @@ -5,6 +5,9 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Page; +use App\Docs\Attributes\Sort; use App\Exceptions\ModelNotSavedException; use App\Libraries\BeatmapsetDiscussion\Discussion; use App\Libraries\BeatmapsetDiscussion\Reply; @@ -15,6 +18,7 @@ use App\Models\Beatmapset; use App\Models\BeatmapsetWatch; use App\Models\User; +use Knuckles\Scribe\Attributes\QueryParam; /** * @group Beatmapset Discussions @@ -63,14 +67,13 @@ public function destroy($id) * posts | [BeatmapsetDiscussionPost](#beatmapsetdiscussionpost)[] | | * users | [User](#user) | | * - * @queryParam beatmapset_discussion_id `id` of the [BeatmapsetDiscussion](#beatmapsetdiscussion). - * @queryParam limit Maximum number of results. - * @queryParam page Search result page. - * @queryParam sort `id_desc` for newest first; `id_asc` for oldest first. Defaults to `id_desc`. - * @queryParam types[] `first`, `reply`, `system` are the valid values. Defaults to `reply`. - * @queryParam user The `id` of the [User](#user). - * @queryParam with_deleted This param has no effect as api calls do not currently receive group permissions. + * @usesCursor + * @queryParam beatmapset_discussion_id integer `id` of the [BeatmapsetDiscussion](#beatmapsetdiscussion). + * @queryParam types string[] `first`, `reply`, `system` are the valid values. Defaults to `reply`. + * @queryParam user integer The `id` of the [User](#user). + * @queryParam with_deleted boolean This param has no effect as api calls do not currently receive group permissions. No-example */ + #[Limit(BeatmapDiscussionPost::PER_PAGE, 5), Page, Sort('IdSort')] public function index() { $bundle = new BeatmapsetDiscussionPostsBundle(request()->all()); diff --git a/app/Http/Controllers/BeatmapDiscussionsController.php b/app/Http/Controllers/BeatmapDiscussionsController.php index ecc1e30e2fe..0474c068eb3 100644 --- a/app/Http/Controllers/BeatmapDiscussionsController.php +++ b/app/Http/Controllers/BeatmapDiscussionsController.php @@ -5,6 +5,9 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Page; +use App\Docs\Attributes\Sort; use App\Exceptions\ModelNotSavedException; use App\Libraries\BeatmapsetDiscussion\Review; use App\Libraries\BeatmapsetDiscussionsBundle; @@ -90,17 +93,15 @@ public function destroy($id) * users | [User](#user)[] | List of users associated with the discussions returned. * * @usesCursor - * @queryParam beatmap_id `id` of the [Beatmap](#beatmap). - * @queryParam beatmapset_id `id` of the [Beatmapset](#beatmapset). - * @queryParam beatmapset_status One of `all`, `ranked`, `qualified`, `disqualified`, `never_qualified`. Defaults to `all`. TODO: better descriptions. - * @queryParam limit Maximum number of results. - * @queryParam message_types[] `suggestion`, `problem`, `mapper_note`, `praise`, `hype`, `review`. Blank defaults to all types. TODO: better descriptions. - * @queryParam only_unresolved `true` to show only unresolved issues; `false`, otherwise. Defaults to `false`. - * @queryParam page Search result page. - * @queryParam sort `id_desc` for newest first; `id_asc` for oldest first. Defaults to `id_desc`. - * @queryParam user The `id` of the [User](#user). - * @queryParam with_deleted This param has no effect as api calls do not currently receive group permissions. + * @queryParam beatmap_id integer `id` of the [Beatmap](#beatmap). + * @queryParam beatmapset_id integer `id` of the [Beatmapset](#beatmapset). + * @queryParam beatmapset_status string One of `all`, `ranked`, `qualified`, `disqualified`, `never_qualified`. Defaults to `all`. TODO: better descriptions. No-example + * @queryParam message_types string[] `suggestion`, `problem`, `mapper_note`, `praise`, `hype`, `review`. Blank defaults to all types. TODO: better descriptions. No-example + * @queryParam only_unresolved boolean `true` to show only unresolved issues; `false`, otherwise. Defaults to `false`. + * @queryParam user integer The `id` of the [User](#user). + * @queryParam with_deleted boolean This param has no effect as api calls do not currently receive group permissions. No-example */ + #[Limit(BeatmapDiscussion::PER_PAGE, 5), Page, Sort('IdSort')] public function index() { $bundle = new BeatmapsetDiscussionsBundle(request()->all()); diff --git a/app/Http/Controllers/BeatmapsController.php b/app/Http/Controllers/BeatmapsController.php index 33b76791953..9394f5b0c58 100644 --- a/app/Http/Controllers/BeatmapsController.php +++ b/app/Http/Controllers/BeatmapsController.php @@ -197,7 +197,7 @@ public function attributes($id) * -------- | ------------------------------------- | ----------- * beatmaps | [BeatmapExtended](#beatmapextended)[] | Includes `beatmapset` (with `ratings`), `failtimes`, and `max_combo`. * - * @queryParam ids[] integer Beatmap IDs to be returned. Specify once for each beatmap ID requested. Up to 50 beatmaps can be requested at once. Example: 1 + * @queryParam ids integer[] Beatmap IDs to be returned. Specify once for each beatmap ID requested. Up to 50 beatmaps can be requested at once. Example: [1] * * @response { * "beatmaps": [ @@ -241,9 +241,9 @@ public function index() * * See [Get Beatmap](#get-beatmap) * - * @queryParam checksum A beatmap checksum. - * @queryParam filename A filename to lookup. - * @queryParam id A beatmap ID to lookup. + * @queryParam checksum string A beatmap checksum. No-example + * @queryParam filename string A filename to lookup. No-example + * @queryParam id integer A beatmap ID to lookup. No-example * * @response "See Beatmap object section" */ @@ -342,9 +342,9 @@ public function show($id) * @urlParam beatmap integer required Id of the [Beatmap](#beatmap). * * @queryParam legacy_only integer Whether or not to exclude lazer scores. Defaults to 0. Example: 0 - * @queryParam mode The [Ruleset](#ruleset) to get scores for. - * @queryParam mods An array of matching Mods, or none // TODO. - * @queryParam type Beatmap score ranking type // TODO. + * @queryParam mode The [Ruleset](#ruleset) to get scores for. Example: osu + * @queryParam mods string[] An array of matching Mods, or none // TODO. No-example + * @queryParam type string Beatmap score ranking type // TODO. No-example */ public function scores($id) { @@ -369,9 +369,9 @@ public function scores($id) * * @urlParam beatmap integer required Id of the [Beatmap](#beatmap). * - * @queryParam mode The [Ruleset](#ruleset) to get scores for. - * @queryParam mods An array of matching Mods, or none // TODO. - * @queryParam type Beatmap score ranking type // TODO. + * @queryParam mode string The [Ruleset](#ruleset) to get scores for. Example: osu + * @queryParam mods string[] An array of matching Mods, or none // TODO. + * @queryParam type string Beatmap score ranking type // TODO. No-example */ public function soloScores($id) { @@ -422,8 +422,8 @@ public function updateOwner($id) * @urlParam user integer required Id of the [User](#user). * * @queryParam legacy_only integer Whether or not to exclude lazer scores. Defaults to 0. Example: 0 - * @queryParam mode The [Ruleset](#ruleset) to get scores for. - * @queryParam mods An array of matching Mods, or none // TODO. + * @queryParam mode string The [Ruleset](#ruleset) to get scores for. Example: osu + * @queryParam mods string[] An array of matching Mods, or none // TODO. No-example */ public function userScore($beatmapId, $userId) { diff --git a/app/Http/Controllers/BeatmapsetDiscussionVotesController.php b/app/Http/Controllers/BeatmapsetDiscussionVotesController.php index 7181060db3a..e1a9df09312 100644 --- a/app/Http/Controllers/BeatmapsetDiscussionVotesController.php +++ b/app/Http/Controllers/BeatmapsetDiscussionVotesController.php @@ -5,7 +5,11 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Page; +use App\Docs\Attributes\Sort; use App\Libraries\BeatmapsetDiscussionVotesBundle; +use App\Models\BeatmapDiscussionVote; /** * @group Beatmapset Discussions @@ -39,15 +43,14 @@ public function __construct() * users | [User](#user) | | * votes | [BeatmapsetDiscussionVote](#beatmapsetdiscussionvote)[] | | * - * @queryParam beatmapset_discussion_id `id` of the [BeatmapsetDiscussion](#beatmapsetdiscussion). - * @queryParam limit Maximum number of results. - * @queryParam page Search result page. - * @queryParam receiver The `id` of the [User](#user) receiving the votes. - * @queryParam score `1` for up vote, `-1` for down vote. - * @queryParam sort `id_desc` for newest first; `id_asc` for oldest first. Defaults to `id_desc`. - * @queryParam user The `id` of the [User](#user) giving the votes. - * @queryParam with_deleted This param has no effect as api calls do not currently receive group permissions. + * @usesCursor + * @queryParam beatmapset_discussion_id integer `id` of the [BeatmapsetDiscussion](#beatmapsetdiscussion). + * @queryParam receiver integer The `id` of the [User](#user) receiving the votes. + * @queryParam score integer `1` for up vote, `-1` for down vote. Example: 1 + * @queryParam user integer The `id` of the [User](#user) giving the votes. + * @queryParam with_deleted boolean This param has no effect as api calls do not currently receive group permissions. No-example */ + #[Limit(BeatmapDiscussionVote::PER_PAGE, 5), Page, Sort('IdSort')] public function index() { $bundle = new BeatmapsetDiscussionVotesBundle(request()->all()); diff --git a/app/Http/Controllers/ChangelogController.php b/app/Http/Controllers/ChangelogController.php index 058e09501cd..38e0897edf3 100644 --- a/app/Http/Controllers/ChangelogController.php +++ b/app/Http/Controllers/ChangelogController.php @@ -65,7 +65,7 @@ private static function changelogEntryMessageIncludes(?array $formats): array * @queryParam max_id integer Maximum build ID. No-example * @queryParam stream string Stream name to return builds from. No-example * @queryParam to string Maximum build version. No-example - * @queryParam message_formats[] string `html`, `markdown`. Default to both. + * @queryParam message_formats[] string `html`, `markdown`. Default to both. No-example * @response { * "streams": [ * { @@ -244,7 +244,7 @@ public function github() * * @urlParam changelog string required Build version, update stream name, or build ID. Example: 20210520.2 * @queryParam key string Unset to query by build version or stream name, or `id` to query by build ID. No-example - * @queryParam message_formats[] string `html`, `markdown`. Default to both. + * @queryParam message_formats string[] `html`, `markdown`. Default to both. * @response See "Get Changelog Build" response. */ public function show($version) diff --git a/app/Http/Controllers/Chat/Channels/MessagesController.php b/app/Http/Controllers/Chat/Channels/MessagesController.php index 23af90f72fd..9ba4574a0e5 100644 --- a/app/Http/Controllers/Chat/Channels/MessagesController.php +++ b/app/Http/Controllers/Chat/Channels/MessagesController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Chat\Channels; +use App\Docs\Attributes\Limit; use App\Http\Controllers\Chat\Controller as BaseController; use App\Libraries\Chat; use App\Models\Chat\Channel; @@ -37,7 +38,6 @@ public function __construct() * Returns an array of [ChatMessage](#chatmessage) * * @urlParam channel integer required The ID of the channel to retrieve messages for - * @queryParam limit integer number of messages to return (max of 50) * @queryParam since integer messages after the specified message id will be returned * @queryParam until integer messages up to but not including the specified message id will be returned * @@ -82,6 +82,7 @@ public function __construct() * } * ] */ + #[Limit] public function index($channelId) { [ diff --git a/app/Http/Controllers/Chat/ChannelsController.php b/app/Http/Controllers/Chat/ChannelsController.php index 372acccd2f9..321ff8bf01e 100644 --- a/app/Http/Controllers/Chat/ChannelsController.php +++ b/app/Http/Controllers/Chat/ChannelsController.php @@ -113,6 +113,9 @@ public function join($channelId, $userId) * This endpoint will only allow the leaving of public channels initially. * * + * @urlParam channel integer required `channel_id` of the [ChatChannel](#chatchannel) to leave. + * @urlParam user integer required `id` of the [User](#user) to leave the channel. + * * @response 204 */ public function part($channelId, $userId) @@ -146,6 +149,8 @@ public function part($channelId, $userId) * channel | [ChatChannel](#chatchannel) | | * users | [User](#user) | Users are only visible for PM channels. * + * @urlParam channel integer required `channel_id` of the [ChatChannel](#chatchannel) to get. + * * @response { * "channel": { * "channel_id": 1337, @@ -191,6 +196,7 @@ public function part($channelId, $userId) * } * ] * } + * */ public function show($channelId) { @@ -314,8 +320,8 @@ public function store() * Note that the read marker cannot be moved backwards - i.e. if a channel has been marked as read up to message_id = 12, you cannot then set it backwards to message_id = 10. It will be rejected. * * - * @queryParam channel_id required The `channel_id` of the channel to mark as read - * @queryParam message_id required The `message_id` of the message to mark as read up to + * @urlParam channel integer required The `channel_id` of the [ChatChannel](#chatchannel) to mark as read. + * @urlParam message integer required The `message_id` of the [ChatMessage](#chatmessage) to mark as read up to. * * @response 204 */ diff --git a/app/Http/Controllers/CommentsController.php b/app/Http/Controllers/CommentsController.php index a85024632c9..cf6b05188ae 100644 --- a/app/Http/Controllers/CommentsController.php +++ b/app/Http/Controllers/CommentsController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Sort; use App\Exceptions\ModelNotSavedException; use App\Jobs\Notifications\CommentNew; use App\Libraries\CommentBundle; @@ -15,6 +16,7 @@ use Carbon\Carbon; use Exception; use Illuminate\Pagination\LengthAwarePaginator; +use Knuckles\Scribe\Attributes\QueryParam; /** * @group Comments @@ -38,6 +40,8 @@ public function __construct() * ### Response Format * * Returns [CommentBundle](#commentbundle) + * + * @urlParam comment integer required The `id` of the [Comment](#comment) */ public function destroy($id) { @@ -67,13 +71,13 @@ public function destroy($id) * * `pinned_comments` is only included when `commentable_type` and `commentable_id` are specified. * - * @queryParam after Return comments which come after the specified comment id as per sort option. No-example - * @queryParam commentable_type The type of resource to get comments for. Example: beatmapset - * @queryParam commentable_id The id of the resource to get comments for. Example: 1 + * @queryParam after integer Return comments which come after the specified comment id as per sort option. No-example + * @queryParam commentable_type string The type of resource to get comments for. Example: beatmapset + * @queryParam commentable_id integer The id of the resource to get comments for. Example: 1 * @queryParam cursor Pagination option. See [CommentSort](#commentsort) for detail. The format follows [Cursor](#cursor) except it's not currently included in the response. No-example - * @queryParam parent_id Limit to comments which are reply to the specified id. Specify 0 to get top level comments. Example: 1 - * @queryParam sort Sort option as defined in [CommentSort](#commentsort). Defaults to `new` for guests and user-specified default when authenticated. Example: new + * @queryParam parent_id integer Limit to comments which are reply to the specified id. Specify 0 to get top level comments. Example: 1 */ + #[Sort('CommentSort', 'Defaults to `new` for guests and user-specified default when authenticated.', 'new')] public function index() { $params = request()->all(); @@ -150,6 +154,8 @@ public function restore($id) * ### Response Format * * Returns [CommentBundle](#commentbundle) + * + * @urlParam comment integer required The `id` of the [Comment](#comment) */ public function show($id) { @@ -177,10 +183,10 @@ public function show($id) * * Returns [CommentBundle](#commentbundle) * - * @queryParam comment.commentable_id Resource ID the comment thread is attached to - * @queryParam comment.commentable_type Resource type the comment thread is attached to - * @queryParam comment.message Text of the comment - * @queryParam comment.parent_id The id of the comment to reply to, null if not a reply + * @queryParam comment.commentable_id integer required Resource ID the comment thread is attached to. Example: 1 + * @queryParam comment.commentable_type string required Resource type the comment thread is attached to. Example: beatmapset + * @queryParam comment.message string required Text of the comment + * @queryParam comment.parent_id integer The id of the comment to reply to, null if not a reply. No-example */ public function store() { @@ -225,7 +231,8 @@ public function store() * * Returns [CommentBundle](#commentbundle) * - * @queryParam comment.message New text of the comment + * @urlParam comment integer required The `id` of the [Comment](#comment) + * @queryParam comment.message string required New text of the comment */ public function update($id) { @@ -277,6 +284,8 @@ public function pinStore($id) * ### Response Format * * Returns [CommentBundle](#commentbundle) + * + * @urlParam comment integer required The `id` of the [Comment](#comment) */ public function voteDestroy($id) { @@ -303,6 +312,8 @@ public function voteDestroy($id) * ### Response Format * * Returns [CommentBundle](#commentbundle) + * + * @urlParam comment integer required The `id` of the [Comment](#comment) */ public function voteStore($id) { diff --git a/app/Http/Controllers/EventsController.php b/app/Http/Controllers/EventsController.php index 14a1a5d4ebf..fd22cb0bf76 100644 --- a/app/Http/Controllers/EventsController.php +++ b/app/Http/Controllers/EventsController.php @@ -7,6 +7,7 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Sort; use App\Models\Event; /** @@ -36,7 +37,6 @@ public function __construct() * events | [Event](#event)[] * * @usesCursor - * @queryParam sort Sorting option. Valid values are `id_desc` (default) and `id_asc`. No-example * * @response { * events: [ @@ -52,6 +52,7 @@ public function __construct() * cursor_string: "eyJldmVudF9pZCI6OH0" * } */ + #[Sort('IdSort')] public function index() { $params = request()->all(); diff --git a/app/Http/Controllers/Forum/TopicsController.php b/app/Http/Controllers/Forum/TopicsController.php index d8c4e34b628..c9d7f872136 100644 --- a/app/Http/Controllers/Forum/TopicsController.php +++ b/app/Http/Controllers/Forum/TopicsController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Forum; +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Sort; use App\Exceptions\ModelNotSavedException; use App\Jobs\Notifications\ForumTopicReply; use App\Libraries\NewForumTopic; @@ -298,7 +300,6 @@ public function reply($id) * @urlParam topic integer required Id of the topic. Example: 1 * * @usesCursor - * @queryParam sort Post sorting option. Valid values are `id_asc` (default) and `id_desc`. No-example * @queryParam limit Maximum number of posts to be returned (20 default, 50 at most). No-example * @queryParam start First post id to be returned with `sort` set to `id_asc`. This parameter is ignored if `cursor_string` is specified. No-example * @queryParam end First post id to be returned with `sort` set to `id_desc`. This parameter is ignored if `cursor_string` is specified. No-example @@ -313,6 +314,7 @@ public function reply($id) * "sort": "id_asc" * } */ + #[Limit(description: 'Maximum number of posts to be returned'), Sort('IdSort')] public function show($id) { $topic = Topic::with(['forum'])->withTrashed()->findOrFail($id); @@ -558,7 +560,7 @@ public function store() * The edited [ForumTopic](#forum-topic). * * @urlParam topic integer required Id of the topic. Example: 1 - * @bodyParam forum_topic[topic_title] string New topic title. Example: titled + * @bodyParam forum_topic[topic_title] string required New topic title. Example: titled */ public function update($id) { diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 0cd6ff043d9..66fb11aab1d 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -6,6 +6,7 @@ namespace App\Http\Controllers; use App; +use App\Docs\Attributes\Page; use App\Libraries\CurrentStats; use App\Libraries\MenuContent; use App\Libraries\Search\AllSearch; @@ -177,8 +178,8 @@ public function quickSearch() * * @queryParam mode string Either `all`, `user`, or `wiki_page`. Default is `all`. Example: all * @queryParam query Search keyword. Example: hello - * @queryParam page Search result page. Ignored for mode `all`. Example: 1 */ + #[Page('Search result page. Ignored for mode `all`')] public function search() { $currentUser = Auth::user(); diff --git a/app/Http/Controllers/MatchesController.php b/app/Http/Controllers/MatchesController.php index f471caf0fa8..6c16c69e9f5 100644 --- a/app/Http/Controllers/MatchesController.php +++ b/app/Http/Controllers/MatchesController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Sort; use App\Models\LegacyMatch\LegacyMatch; use App\Models\User; use App\Transformers\LegacyMatch\EventTransformer; @@ -38,8 +40,6 @@ public function __construct() * params.sort | string | | * * @usesCursor - * @queryParam limit integer Maximum number of matches (50 default, 1 minimum, 50 maximum). No-example - * @queryParam sort string `id_desc` for newest first; `id_asc` for oldest first. Defaults to `id_desc`. No-example * @response { * "matches": [ * { @@ -60,6 +60,7 @@ public function __construct() * "cursor_string": "eyJtYXRjaF9pZCI6MTE0NDI4Njg1fQ" * } */ + #[Limit, Sort('IdSort')] public function index() { $params = request()->all(); @@ -99,7 +100,6 @@ public function index() * @urlParam match integer required Match ID. No-example * @queryParam before integer Filter for match events before the specified [MatchEvent.id](#matchevent). No-example * @queryParam after integer Filter for match events after the specified [MatchEvent.id](#matchevent). No-example - * @queryParam limit integer Maximum number of match events (100 default, 1 minimum, 101 maximum). No-example * @response { * "match": { * "id": 16155689, @@ -124,6 +124,7 @@ public function index() * "current_game_id": null * } */ + #[Limit(100, 1, 101)] public function show($id) { $match = LegacyMatch::findOrFail($id); diff --git a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php index ea1f7321d1f..fbfeb0fb4d3 100644 --- a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php +++ b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Multiplayer\Rooms\Playlist; +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Sort; use App\Exceptions\InvariantException; use App\Http\Controllers\Controller as BaseController; use App\Libraries\ClientCheck; @@ -43,9 +45,9 @@ public function __construct() * @urlParam playlist integer required Id of the playlist item. * * @usesCursor - * @queryParam limit Number of scores to be returned. - * @queryParam sort [MultiplayerScoresSort](#multiplayerscoressort) parameter. */ + #[Limit] + #[Sort('MultiplayerScoresSort')] public function index($roomId, $playlistId) { $playlist = PlaylistItem::where('room_id', $roomId)->findOrFail($playlistId); diff --git a/app/Http/Controllers/Multiplayer/RoomsController.php b/app/Http/Controllers/Multiplayer/RoomsController.php index 33e7e7c13c4..0072a766c29 100644 --- a/app/Http/Controllers/Multiplayer/RoomsController.php +++ b/app/Http/Controllers/Multiplayer/RoomsController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Multiplayer; +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Sort; use App\Exceptions\InvariantException; use App\Http\Controllers\Controller; use App\Http\Controllers\Ranking\DailyChallengeController; @@ -34,12 +36,11 @@ public function destroy($id) * * @group Multiplayer * - * @queryParam limit Maximum number of results. No-example * @queryParam mode Filter mode; `active` (default), `all`, `ended`, `participated`, `owned`. No-example * @queryParam season_id Season ID to return Rooms from. No-example - * @queryParam sort Sort order; `ended`, `created`. No-example * @queryParam type_group `playlists` (default) or `realtime`. No-example */ + #[Limit(250, 1, 250), Sort(['ended', 'created'])] public function index() { $apiVersion = api_version(); diff --git a/app/Http/Controllers/NewsController.php b/app/Http/Controllers/NewsController.php index 8fa04aeb12a..f77fa4945a9 100644 --- a/app/Http/Controllers/NewsController.php +++ b/app/Http/Controllers/NewsController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Limit; use App\Libraries\CommentBundle; use App\Models\NewsPost; @@ -37,7 +38,6 @@ class NewsController extends Controller * * * @usesCursor - * @queryParam limit integer Maximum number of posts (12 default, 1 minimum, 21 maximum). No-example * @queryParam year integer Year to return posts from. No-example * @response { * "news_posts": [ @@ -78,6 +78,7 @@ class NewsController extends Controller * "cursor_string": "WyJodHRwczpcL1wvd3d3LnlvdXR1YmUuY29tXC93YXRjaD92PWRRdzR3OVdnWGNRIl0" * } */ + #[Limit(12, 1, 21)] public function index() { $params = request()->all(); diff --git a/app/Http/Controllers/NotificationsController.php b/app/Http/Controllers/NotificationsController.php index b65192d69e0..b0b79b5cb91 100644 --- a/app/Http/Controllers/NotificationsController.php +++ b/app/Http/Controllers/NotificationsController.php @@ -56,7 +56,7 @@ public function endpoint() * unread_count | total unread notifications * notification_endpoint | url to connect to websocket server * - * @queryParam max_id Maximum `id` fetched. Can be used to load earlier notifications. Defaults to no limit (fetch latest notifications) + * @queryParam max_id integer Maximum `id` fetched. Can be used to load earlier notifications. Defaults to no limit (fetch latest notifications). No-example * * @response { * "has_more": true, diff --git a/app/Http/Controllers/RankingController.php b/app/Http/Controllers/RankingController.php index 51980dc82d8..da357464c8d 100644 --- a/app/Http/Controllers/RankingController.php +++ b/app/Http/Controllers/RankingController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Page; use App\Models\Beatmap; use App\Models\Country; use App\Models\CountryStatistics; @@ -151,10 +152,10 @@ public static function url( * @urlParam type string required [RankingType](#rankingtype). Example: performance * * @queryParam country string Filter ranking by country code. Only available for `type` of `performance`. Example: JP - * @queryParam cursor [Cursor](#cursor). No-example - * @queryParam filter Either `all` (default) or `friends`. Example: all - * @queryParam spotlight The id of the spotlight if `type` is `charts`. Ranking for latest spotlight will be returned if not specified. No-example - * @queryParam variant Filter ranking to specified mode variant. For `mode` of `mania`, it's either `4k` or `7k`. Only available for `type` of `performance`. Example: 4k + * @queryParam cursor.page integer See [Cursor](#cursor). No-example + * @queryParam filter string Either `all` (default) or `friends`. Example: all + * @queryParam spotlight integer The id of the spotlight if `type` is `charts`. Ranking for latest spotlight will be returned if not specified. No-example + * @queryParam variant string Filter ranking to specified mode variant. For `mode` of `mania`, it's either `4k` or `7k`. Only available for `type` of `performance`. Example: 4k */ public function index($mode, $type) { @@ -296,9 +297,8 @@ public function index($mode, $type) * Field | Type | Description * ------- | --------------- | ----------- * ranking | [User](#user)[] | Includes `kudosu`. - * - * @queryParam page Ranking page. Example: 1 */ + #[Page('Ranking page')] public function kudosu() { static $maxResults = 1000; diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php index cd72c10642a..73ea2a0abfc 100644 --- a/app/Http/Controllers/UsersController.php +++ b/app/Http/Controllers/UsersController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers; +use App\Docs\Attributes\Limit; +use App\Docs\Attributes\Offset; use App\Exceptions\ModelNotSavedException; use App\Exceptions\ValidationException; use App\Http\Middleware\RequestCost; @@ -280,9 +282,6 @@ public function storeWeb() * @urlParam user integer required Id of the user. Example: 1 * @urlParam type string required Beatmap type. Example: favourite * - * @queryParam limit Maximum number of results. - * @queryParam offset Result offset for pagination. Example: 1 - * * @response [ * { * "id": 1, @@ -294,6 +293,7 @@ public function storeWeb() * } * ] */ + #[Limit(5, 1, 100), Offset] public function beatmapsets($_userId, $type) { static $mapping = [ @@ -332,7 +332,7 @@ public function beatmapsets($_userId, $type) * ----- | --------------- | ----------- * users | [User](#user)[] | Includes `country`, `cover`, `groups`, and `statistics_rulesets`. * - * @queryParam ids[] User id to be returned. Specify once for each user id requested. Up to 50 users can be requested at once. Example: 1 + * @queryParam ids integer[] `id`s of users to be returned. Specify once for each user id requested. Up to 50 users can be requested at once. Example: [1,2] * @queryParam include_variant_statistics boolean Whether to additionally include `statistics_rulesets.variants` (default: `false`). No-example * * @response { @@ -430,9 +430,6 @@ public function posts($id) * * @urlParam user integer required Id of the user. Example: 1 * - * @queryParam limit Maximum number of results. - * @queryParam offset Result offset for pagination. Example: 1 - * * @response [ * { * "id": 1, @@ -444,6 +441,7 @@ public function posts($id) * } * ] */ + #[Limit(5, 1, 100), Offset] public function kudosu($_userId) { return $this->getExtra('recentlyReceivedKudosu', [], $this->perPage, $this->offset); @@ -461,10 +459,7 @@ public function kudosu($_userId) * Array of [Event](#event). * * @urlParam user integer required Id of the user. Example: 1 - * - * @queryParam limit Maximum number of results. - * @queryParam offset Result offset for pagination. Example: 1 - * + * * * @response [ * { * "id": 1, @@ -476,6 +471,7 @@ public function kudosu($_userId) * } * ] */ + #[Limit(5, 1, 100), Offset] public function recentActivity($_userId) { return $this->getExtra('recentActivity', [], $this->perPage, $this->offset); @@ -503,10 +499,8 @@ public function recentActivity($_userId) * @urlParam type string required Score type. Must be one of these: `best`, `firsts`, `recent`. Example: best * * @queryParam legacy_only integer Whether or not to exclude lazer scores. Defaults to 0. Example: 0 - * @queryParam include_fails Only for recent scores, include scores of failed plays. Set to 1 to include them. Defaults to 0. Example: 0 - * @queryParam mode [Ruleset](#ruleset) of the scores to be returned. Defaults to the specified `user`'s mode. Example: osu - * @queryParam limit Maximum number of results. - * @queryParam offset Result offset for pagination. Example: 1 + * @queryParam include_fails integer Only for recent scores, include scores of failed plays. Set to 1 to include them. Defaults to 0. Example: 0 + * @queryParam mode string [Ruleset](#ruleset) of the scores to be returned. Defaults to the specified `user`'s mode. Example: osu * * @response [ * { @@ -519,6 +513,7 @@ public function recentActivity($_userId) * } * ] */ + #[Limit(5, 1, 100), Offset] public function scores($_userId, $type) { static $mapping = [ @@ -594,10 +589,6 @@ public function me($mode = null) * * This endpoint returns the detail of specified user. * - * - * * --- * * ### Response format @@ -636,7 +627,7 @@ public function me($mode = null) * @urlParam user integer required Id or `@`-prefixed username of the user. Previous usernames are also checked in some cases. Example: 1 * @urlParam mode string [Ruleset](#ruleset). User default mode will be used if not specified. Example: osu * - * @queryParam key Type of `user` passed in url parameter. Can be either `id` or `username` to limit lookup by their respective type. Passing empty or invalid value will result in id lookup followed by username lookup if not found. This parameter has been deprecated. Prefix `user` parameter with `@` instead to lookup by username. + * @queryParam key string Type of `user` passed in url parameter. Can be either `id` or `username` to limit lookup by their respective type. Passing empty or invalid value will result in id lookup followed by username lookup if not found. This parameter has been deprecated. Prefix `user` parameter with `@` instead to lookup by username. No-example * * @response "See User object section" */ diff --git a/config/scribe.php b/config/scribe.php index 38ea83d10ad..815758cf403 100644 --- a/config/scribe.php +++ b/config/scribe.php @@ -300,17 +300,20 @@ 'strategies' => [ 'metadata' => [ Strategies\Metadata\GetFromDocBlocks::class, + Strategies\Metadata\GetFromMetadataAttributes::class, ], 'urlParameters' => [ Strategies\UrlParameters\GetFromLaravelAPI::class, Strategies\UrlParameters\GetFromLumenAPI::class, + Strategies\UrlParameters\GetFromUrlParamAttribute::class, Strategies\UrlParameters\GetFromUrlParamTag::class, ], 'queryParameters' => [ + App\Docs\Strategies\GetFromQueryParamAttribute::class, + App\Docs\Strategies\UsesCursor::class, Strategies\QueryParameters\GetFromQueryParamTag::class, Strategies\QueryParameters\GetFromFormRequest::class, Strategies\QueryParameters\GetFromInlineValidator::class, - App\Docs\Strategies\UsesCursor::class, ], 'headers' => [ Strategies\Headers\GetFromRouteRules::class, diff --git a/resources/views/docs/_structures/id_sort.md b/resources/views/docs/_structures/id_sort.md new file mode 100644 index 00000000000..4760b96f4b3 --- /dev/null +++ b/resources/views/docs/_structures/id_sort.md @@ -0,0 +1,9 @@ +## IdSort + +Available sort types are `id_asc`, `id_desc`. + +Name | Description +------- | ---------------------------- +id_asc | Sort by oldest first. +id_desc | Sort by newest first. + diff --git a/resources/views/docs/_websocket_events.md b/resources/views/docs/_websocket_events.md index c0edb63d259..9b1aaa24c0a 100644 --- a/resources/views/docs/_websocket_events.md +++ b/resources/views/docs/_websocket_events.md @@ -48,7 +48,7 @@ Broadcast to the user when the user joins a chat channel. ### Payload Format -[ChatChannel](#chat-channel) with `current_user_attributes`, `last_message_id`, `users` additional attributes. +[ChatChannel](#chatchannel) with `current_user_attributes`, `last_message_id`, `users` additional attributes. ## chat.channel.part @@ -56,7 +56,7 @@ Broadcast to the user when the user leaves a chat channel. ### Payload Format -[ChatChannel](#chat-channel) with `current_user_attributes`, `last_message_id`, `users` additional attributes. +[ChatChannel](#chatchannel) with `current_user_attributes`, `last_message_id`, `users` additional attributes. ## chat.message.new