-
-
Notifications
You must be signed in to change notification settings - Fork 130
feat(api): add API_GetUserSetRequests #2950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 28 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
b29f42c
feat(api): get set request list for user
drisc 345a357
feat(api): add new factory and test
drisc 6e9057b
fix: added HasFactory to model
drisc 41c7aa4
chore: lint
drisc 882911a
fix: composer analyse issues
drisc 15c0c7d
fix: lint
drisc ced6d8c
Merge branch 'RetroAchievements:master' into setrequest-api
drisc 235d470
fix: lift queries out of forloop
drisc 967c9e1
nit: hardcoded assert values based on feedback
drisc 5dd2a23
fix(style): moved newFactory() after $casts
drisc 4d42e8b
nit: added default value for type to usergamelistentry factory
drisc c080cda
nit: added 404 response to empty user request
drisc 5a6414a
refactor: added input validation for query param
drisc 2e5126e
refactor: condensed set request query significantly
drisc 7e22df5
fix: update test assertion for 404 response
drisc e38ab0f
Merge branch 'master' into setrequest-api
drisc d9eff95
refactor(api): optimise query based on feedback
drisc 5a52888
refactor(test): adjust test for new query sorting
drisc 2067907
Merge branch 'master' into setrequest-api
drisc 351c0b4
Merge branch 'master' into setrequest-api
wescopeland 8604e13
refactor(api): stricter query validation, remove map
drisc 46d4cb9
Merge branch 'master' into setrequest-api
drisc a3036e3
Merge branch 'master' into setrequest-api
Jamiras 436f70c
Merge branch 'master' into setrequest-api
drisc a678a8f
feat: added type flag to return all requests
drisc 29dbab7
refactor(test): added test for new type arg
drisc c7ca65c
chore: lint
drisc 45718e5
fix: applied correct input validation to type query
drisc 9be5678
refactor: applied suggested code squash
drisc 114a3a9
Merge branch 'master' into setrequest-api
drisc 1177b27
nit: typo
drisc ea54b86
refactor: change magic string to enum
drisc 32fd885
refactor(test): change magic string to enum, update function name
drisc 041999e
Merge branch 'master' into setrequest-api
drisc 06dbbff
Merge branch 'master' into setrequest-api
wescopeland File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Database\Factories; | ||
|
|
||
| use App\Models\Game; | ||
| use App\Models\User; | ||
| use App\Models\UserGameListEntry; | ||
| use Illuminate\Database\Eloquent\Factories\Factory; | ||
|
|
||
| /** | ||
| * @extends Factory<UserGameListEntry> | ||
| */ | ||
| class UserGameListEntryFactory extends Factory | ||
| { | ||
| protected $model = UserGameListEntry::class; | ||
|
|
||
| public function definition(): array | ||
| { | ||
| return [ | ||
| 'user_id' => User::factory(), | ||
| 'type' => 'achievement_set_request', | ||
| 'GameID' => Game::factory(), | ||
| ]; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| <?php | ||
|
|
||
| /* | ||
| * API_GetUserSetRequests - gets user's set request list | ||
| * u : username | ||
| * t : type. 0 = only active, 1 = all requests including completed ones (default: 0) | ||
| * | ||
| * array RequestedSets | ||
| * object [value] | ||
| * int GameID unique identifier of the game | ||
| * string Title set title | ||
| * int ConsoleID the id of the console that the set is for | ||
| * string ConsoleName the name of the console that the set is for | ||
| * string ImageIcon the url of the sets icon | ||
| * int TotalRequests maximum number of requests a user has | ||
| * int PointsForNext number of points remaining until maximum request increase | ||
| */ | ||
|
|
||
| use App\Models\User; | ||
| use App\Models\UserGameListEntry; | ||
| use App\Support\Rules\CtypeAlnum; | ||
| use Illuminate\Support\Arr; | ||
| use Illuminate\Support\Facades\Validator; | ||
|
|
||
| $input = Validator::validate(Arr::wrap(request()->query()), [ | ||
| 'u' => ['required', 'min:2', 'max:20', new CtypeAlnum()], | ||
| 't' => ['nullable', 'in:0,1'], | ||
| ]); | ||
|
|
||
| $user = User::firstWhere('User', request()->query('u')); | ||
| if (!$user) { | ||
| return response()->json([], 404); | ||
| } | ||
|
|
||
| $type = (int) request()->query('t'); | ||
|
|
||
| if ($type === 1) { | ||
| $requestedSets = UserGameListEntry::select([ | ||
| 'GameData.id as GameID', | ||
| 'GameData.Title', | ||
| 'GameData.ImageIcon', | ||
| 'GameData.ConsoleID', | ||
| 'Console.Name as ConsoleName', | ||
| ]) | ||
| ->join('GameData', 'SetRequest.GameID', '=', 'GameData.ID') | ||
| ->join('Console', 'GameData.ConsoleID', '=', 'Console.ID') | ||
| ->where('SetRequest.user_id', $user->id) | ||
| ->where('type', 'achievement_set_request') | ||
| ->orderBy('GameData.sort_title') | ||
| ->get() | ||
| ->toArray(); | ||
| } else { | ||
| $requestedSets = UserGameListEntry::select([ | ||
| 'GameData.id as GameID', | ||
| 'GameData.Title', | ||
| 'GameData.ImageIcon', | ||
| 'GameData.ConsoleID', | ||
| 'Console.Name as ConsoleName', | ||
| ]) | ||
| ->join('GameData', 'SetRequest.GameID', '=', 'GameData.ID') | ||
| ->join('Console', 'GameData.ConsoleID', '=', 'Console.ID') | ||
| ->where('SetRequest.user_id', $user->id) | ||
| ->where('type', 'achievement_set_request') | ||
| ->where('GameData.achievements_published', '=', '0') | ||
| ->orderBy('GameData.sort_title') | ||
| ->get() | ||
| ->toArray(); | ||
| } | ||
|
Jamiras marked this conversation as resolved.
|
||
|
|
||
| $userRequestInfo = UserGameListEntry::getUserSetRequestsInformation($user); | ||
|
|
||
| return response()->json([ | ||
| 'RequestedSets' => $requestedSets, | ||
| 'TotalRequests' => $userRequestInfo['total'], | ||
| 'PointsForNext' => $userRequestInfo['pointsForNext'], | ||
| ]); | ||
|
wescopeland marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Tests\Feature\Api\V1; | ||
|
|
||
| use App\Models\Game; | ||
| use App\Models\System; | ||
| use App\Models\User; | ||
| use App\Models\UserGameListEntry; | ||
| use Illuminate\Foundation\Testing\RefreshDatabase; | ||
| use Tests\TestCase; | ||
|
|
||
| class UserSetRequestsTest extends TestCase | ||
| { | ||
| use RefreshDatabase; | ||
| use BootstrapsApiV1; | ||
|
|
||
| /** | ||
| * Test that the API returns an empty response for a non-existent user. | ||
| */ | ||
| public function testGetUserSetRequestsUnknownUser(): void | ||
| { | ||
| $this->get($this->apiUrl('GetUserSetRequests', ['u' => 'nonExistant'])) | ||
| ->assertStatus(404) | ||
| ->assertJson([]); | ||
| } | ||
|
|
||
| /** | ||
| * Test that the API returns all set requests for an existing user. | ||
| */ | ||
| public function testGetAllUserSetRequests(): void | ||
| { | ||
| $system = System::factory()->create(); | ||
| $game = Game::factory()->create([ | ||
| 'ConsoleID' => $system->ID, | ||
| 'ImageIcon' => '/Images/001234.png', | ||
| 'achievements_published' => 0, | ||
|
|
||
| ]); | ||
| $game2 = Game::factory()->create([ | ||
| 'Title' => '~Hack~ Test Case', | ||
| 'ConsoleID' => $system->ID, | ||
| 'ImageIcon' => '/Images/001235.png', | ||
| 'achievements_published' => 12, | ||
| ]); | ||
|
|
||
| $user = User::factory()->create([ | ||
| 'RAPoints' => 1501, // enough points to set request total to 1 | ||
| ]); | ||
|
|
||
| // Create the first user game list entry for the set request | ||
| UserGameListEntry::factory()->create([ | ||
| 'user_id' => $user->ID, | ||
| 'type' => 'achievement_set_request', | ||
| 'GameID' => $game->ID, | ||
| ]); | ||
| // Create the second user game list entry for the set request | ||
| UserGameListEntry::factory()->create([ | ||
| 'user_id' => $user->ID, | ||
| 'type' => 'achievement_set_request', | ||
| 'GameID' => $game2->ID, | ||
| ]); | ||
|
|
||
| $this->get($this->apiUrl('GetUserSetRequests', ['u' => $user->User, 't' => 1])) | ||
| ->assertSuccessful() | ||
| ->assertJson([ | ||
| 'RequestedSets' => [ | ||
| [ | ||
| 'GameID' => $game->ID, | ||
| 'Title' => $game->Title, | ||
| 'ConsoleID' => $game->ConsoleID, | ||
| 'ConsoleName' => $system->Name, | ||
| 'ImageIcon' => $game->ImageIcon, | ||
| ], | ||
| [ | ||
| 'GameID' => $game2->ID, | ||
| 'Title' => $game2->Title, | ||
| 'ConsoleID' => $game2->ConsoleID, | ||
| 'ConsoleName' => $system->Name, | ||
| 'ImageIcon' => $game2->ImageIcon, | ||
| ], | ||
| ], | ||
| 'TotalRequests' => 1, | ||
| 'PointsForNext' => 999, | ||
| ]); | ||
| } | ||
|
|
||
| /** | ||
| * Test that the API returns only set requests with no published achievements for an existing user. | ||
| */ | ||
| public function testGetUserSetRequests(): void | ||
|
wescopeland marked this conversation as resolved.
Outdated
|
||
| { | ||
| $system = System::factory()->create(); | ||
| $game = Game::factory()->create([ | ||
| 'ConsoleID' => $system->ID, | ||
| 'ImageIcon' => '/Images/001234.png', | ||
| 'achievements_published' => 4, | ||
| ]); | ||
| $game2 = Game::factory()->create([ | ||
| 'Title' => '~Hack~ Test Case', | ||
| 'ConsoleID' => $system->ID, | ||
| 'ImageIcon' => '/Images/001235.png', | ||
| 'achievements_published' => 0, | ||
| ]); | ||
|
|
||
| $user = User::factory()->create([ | ||
| 'RAPoints' => 1501, // enough points to set request total to 1 | ||
| ]); | ||
|
|
||
| // Create the first user game list entry for the set request | ||
| UserGameListEntry::factory()->create([ | ||
| 'user_id' => $user->ID, | ||
| 'type' => 'achievement_set_request', | ||
| 'GameID' => $game->ID, | ||
| ]); | ||
| // Create the second user game list entry for the set request | ||
| UserGameListEntry::factory()->create([ | ||
| 'user_id' => $user->ID, | ||
| 'type' => 'achievement_set_request', | ||
| 'GameID' => $game2->ID, | ||
| ]); | ||
|
|
||
| // Note that only the second game is present in the assert. The code will find the first game as well but only return the game with no published achievements. | ||
|
drisc marked this conversation as resolved.
Outdated
|
||
| $this->get($this->apiUrl('GetUserSetRequests', ['u' => $user->User, 't' => 0])) | ||
| ->assertSuccessful() | ||
| ->assertJson([ | ||
| 'RequestedSets' => [ | ||
| [ | ||
| 'GameID' => $game2->ID, | ||
| 'Title' => $game2->Title, | ||
| 'ConsoleID' => $game2->ConsoleID, | ||
| 'ConsoleName' => $system->Name, | ||
| 'ImageIcon' => $game2->ImageIcon, | ||
| ], | ||
| ], | ||
| 'TotalRequests' => 1, | ||
| 'PointsForNext' => 999, | ||
| ]); | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.