From fabe6a833feefa374fe53ad8c718e054838bfd85 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 09:19:51 -0400 Subject: [PATCH 01/28] adding comment factory --- app/Models/Comment.php | 2 ++ database/factories/CommentFactory.php | 29 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 database/factories/CommentFactory.php diff --git a/app/Models/Comment.php b/app/Models/Comment.php index 52f58262ae..78111af081 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -10,12 +10,14 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Laravel\Scout\Searchable; class Comment extends BaseModel { use Searchable; use SoftDeletes; + use HasFactory; // TODO rename Comment table to comments // TODO rename ID column id diff --git a/database/factories/CommentFactory.php b/database/factories/CommentFactory.php new file mode 100644 index 0000000000..b8b4b3d46c --- /dev/null +++ b/database/factories/CommentFactory.php @@ -0,0 +1,29 @@ + $this->faker->paragraph, + 'Submitted' => $this->faker->dateTimeBetween('-1 year', 'now'), + 'Edited' => $this->faker->dateTimeBetween('now', '+1 year'), + 'user_id' => 1, + 'commentable_id' => $this->faker->numberBetween(1, 100), + 'commentable_type' => $this->faker->randomElement([ + 'App\Models\Article', + 'App\Models\Post', + 'App\Models\Product', + ]), + ]; + } +} From b11ab6a667d5283b41561e192a7e8cc037e5c772 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 09:20:03 -0400 Subject: [PATCH 02/28] adding endpoint to get comments --- public/API/API_GetComments.php | 76 +++++++++++ tests/Feature/Api/V1/GetCommentsTest.php | 165 +++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 public/API/API_GetComments.php create mode 100644 tests/Feature/Api/V1/GetCommentsTest.php diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php new file mode 100644 index 0000000000..6d7bad4771 --- /dev/null +++ b/public/API/API_GetComments.php @@ -0,0 +1,76 @@ +query()), [ + 'i' => ['sometimes', 'integer'], + 'u' => ['sometimes', 'string'], + 't' => ['required', 'integer'], + 'o' => ['sometimes', 'integer', 'min:0', 'nullable'], + 'c' => ['sometimes', 'integer', 'min:1', 'max:500', 'nullable'], +]); + +$offset = $input['o'] ?? 0; +$count = $input['c'] ?? 100; + +$gameOrAchievementId = (int) request()->query('i'); +$username = (string) request()->query('u'); +$commentType = (int) request()->query('t'); + +if ($username) { + $userId = User::firstWhere('User', $username); + + $comments = Comment::where(ArticleType::class, $commentType) + ->offset($offset) + ->limit($count) + ->where(ArticleID::class, $userId->ID) + ->get(); +} else { + $comments = Comment::where(ArticleType::class, $commentType) + ->offset($offset) + ->limit($count) + ->where(ArticleID::class, $gameOrAchievementId) + ->get(); +} + +$results = []; + +if (!empty($comments)) { + foreach ($comments as $nextComment) { + $user = User::firstWhere('ID', $nextComment['user_id']); + if ($user) { + $commentData = [ + 'User' => $user->username, + 'Submitted' => $nextComment->Submitted, + 'CommentText' => $nextComment->Payload, + ]; + + array_push($results, $commentData); + } + } +} + +return response()->json([ + 'Count' => count($results), + 'Total' => count($comments), + 'Results' => $results, +]); diff --git a/tests/Feature/Api/V1/GetCommentsTest.php b/tests/Feature/Api/V1/GetCommentsTest.php new file mode 100644 index 0000000000..42ebcff3f0 --- /dev/null +++ b/tests/Feature/Api/V1/GetCommentsTest.php @@ -0,0 +1,165 @@ +get($this->apiUrl('GetComments')) + ->assertJsonValidationErrors([ + 'u', + ]); + } + + public function testGetUserCompletionProgressUnknownUser(): void + { + $this->get($this->apiUrl('GetComments', ['u' => 'nonExistant'])) + ->assertSuccessful() + ->assertJson([]); + } + + public function testGetCommentsForAchievement() + { + // Arrange + $system = System::factory()->create(); + $game = Game::factory()->create(['ConsoleID' => $system->ID]); + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + + $achievement = Achievement::factory()->create(['GameID' => $game->ID, 'user_id' => $user1->ID]); + $comment1 = Comment::factory()->create([ + 'ArticleID' => $achievement->ID, + 'ArticleType' => 2, + 'user_id' => $user1->ID, + 'Payload' => 'This is a great achievement!', + ]); + $comment2 = Comment::factory()->create([ + 'ArticleID' => $achievement->ID, + 'ArticleType' => 2, + 'user_id' => $user2->ID, + 'Payload' => 'I agree, this is awesome!', + ]); + + // Act + $response = $this->get($this->apiUrl('GetComments', ['i' => $achievement->ID, 't' => 2])) + ->assertSuccessful(); + + // Assert + $response->assertStatus(200); + $response->assertJsonStructure([ + 'Count', + 'Total', + 'Results' => [ + '*' => [ + 'User', + 'Submitted', + 'CommentText', + ], + ], + ]); + $this->assertCount(2, $response->json('Results')); + $this->assertEquals($user1->User, $response->json('Results.0.User')); + $this->assertEquals($comment1->Payload, $response->json('Results.0.CommentText')); + $this->assertEquals($user2->User, $response->json('Results.1.User')); + $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); + } + + public function testGetCommentsForGame() + { + // Arrange + $system = System::factory()->create(); + $game = Game::factory()->create(['ConsoleID' => $system->ID]); + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $comment1 = Comment::factory()->create([ + 'ArticleID' => $game->ID, + 'ArticleType' => 1, + 'user_id' => $user1->ID, + 'Payload' => 'This is a great achievement!', + ]); + $comment2 = Comment::factory()->create([ + 'ArticleID' => $game->ID, + 'ArticleType' => 1, + 'user_id' => $user2->ID, + 'Payload' => 'I agree, this is awesome!', + ]); + + // Act + $response = $this->get($this->apiUrl('GetComments', ['i' => $game->ID, 't' => 1])) + ->assertSuccessful(); + + // Assert + $response->assertStatus(200); + $response->assertJsonStructure([ + 'Count', + 'Total', + 'Results' => [ + '*' => [ + 'User', + 'Submitted', + 'CommentText', + ], + ], + ]); + $this->assertCount(2, $response->json('Results')); + $this->assertEquals($user1->username, $response->json('Results.0.User')); + $this->assertEquals($comment1->Payload, $response->json('Results.0.CommentText')); + $this->assertEquals($user2->username, $response->json('Results.1.User')); + $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); + } + + public function testGetCommentsForUser() + { + // Arrange + $user = User::factory()->create(); + $user2 = User::factory()->create(); + $comment1 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 3, + 'user_id' => $user2->ID, + 'Payload' => 'This is my first comment.', + ]); + $comment2 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 3, + 'user_id' => $user2->ID, + 'Payload' => 'This is my second comment.', + ]); + + // Act + $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) + ->assertSuccessful(); + + // Assert + $response->assertStatus(200); + $response->assertJsonStructure([ + 'Count', + 'Total', + 'Results' => [ + '*' => [ + 'User', + 'Submitted', + 'CommentText', + ], + ], + ]); + $this->assertCount(2, $response->json('Results')); + $this->assertEquals($user2->User, $response->json('Results.0.User')); + $this->assertEquals($comment1->Payload, $response->json('Results.0.CommentText')); + $this->assertEquals($user2->User, $response->json('Results.1.User')); + $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); + } +} From cca9d3b9223ee781456c696e00ad13262543eaf9 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 09:21:56 -0400 Subject: [PATCH 03/28] pint --- app/Models/Comment.php | 2 +- database/factories/CommentFactory.php | 2 -- tests/Feature/Api/V1/GetCommentsTest.php | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/Models/Comment.php b/app/Models/Comment.php index 78111af081..2ce4157e3b 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -6,11 +6,11 @@ use App\Support\Database\Eloquent\BaseModel; use Exception; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Database\Eloquent\Factories\HasFactory; use Laravel\Scout\Searchable; class Comment extends BaseModel diff --git a/database/factories/CommentFactory.php b/database/factories/CommentFactory.php index b8b4b3d46c..4a9ce14c5e 100644 --- a/database/factories/CommentFactory.php +++ b/database/factories/CommentFactory.php @@ -3,9 +3,7 @@ namespace Database\Factories; use App\Models\Comment; -use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; -use Illuminate\Database\Eloquent\Model; class CommentFactory extends Factory { diff --git a/tests/Feature/Api/V1/GetCommentsTest.php b/tests/Feature/Api/V1/GetCommentsTest.php index 42ebcff3f0..29fe9a9565 100644 --- a/tests/Feature/Api/V1/GetCommentsTest.php +++ b/tests/Feature/Api/V1/GetCommentsTest.php @@ -1,7 +1,6 @@ Date: Wed, 10 Jul 2024 09:23:15 -0400 Subject: [PATCH 04/28] composer analyse --- database/factories/CommentFactory.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/database/factories/CommentFactory.php b/database/factories/CommentFactory.php index 4a9ce14c5e..1a0346de06 100644 --- a/database/factories/CommentFactory.php +++ b/database/factories/CommentFactory.php @@ -1,10 +1,15 @@ + */ class CommentFactory extends Factory { protected $model = Comment::class; From 7aa8b5afe588bf80e3a0624321be8be9b15a00de Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 09:25:18 -0400 Subject: [PATCH 05/28] composer analyse --- public/API/API_GetComments.php | 1 + tests/Feature/Api/V1/GetCommentsTest.php | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index 6d7bad4771..2e7c79621e 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -1,5 +1,6 @@ assertJson([]); } - public function testGetCommentsForAchievement() + public function testGetCommentsForAchievement(): void { // Arrange $system = System::factory()->create(); @@ -76,7 +76,7 @@ public function testGetCommentsForAchievement() $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); } - public function testGetCommentsForGame() + public function testGetCommentsForGame(): void { // Arrange $system = System::factory()->create(); @@ -120,7 +120,7 @@ public function testGetCommentsForGame() $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); } - public function testGetCommentsForUser() + public function testGetCommentsForUser(): void { // Arrange $user = User::factory()->create(); From b77f9abe1e14ff5e56586265d176f1bb17a84118 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 09:28:13 -0400 Subject: [PATCH 06/28] stop using class for ArticleType and ArticleID --- public/API/API_GetComments.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index 2e7c79621e..ddb6377b77 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -1,9 +1,9 @@ offset($offset) ->limit($count) - ->where(ArticleID::class, $userId->ID) + ->where('ArticleID', $userId->ID) ->get(); } else { - $comments = Comment::where(ArticleType::class, $commentType) + $comments = Comment::where('ArticleType', $commentType) ->offset($offset) ->limit($count) - ->where(ArticleID::class, $gameOrAchievementId) + ->where('ArticleID', $gameOrAchievementId) ->get(); } From a80b3706e2a6373113e3c8a5b816362adf8231be Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 09:28:25 -0400 Subject: [PATCH 07/28] stop using class for ArticleType and ArticleID --- public/API/API_GetComments.php | 1 - 1 file changed, 1 deletion(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index ddb6377b77..e2317d5074 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -3,7 +3,6 @@ use App\Models\Achievement; use App\Models\Comment; use App\Models\User; -use App\Community\Enums\ArticleType; /* * API_GetComments - returns the comments associated to a game or achievement From 29d3e2f77c88966975785ca1ae7a9a839068a501 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 13:31:05 +0000 Subject: [PATCH 08/28] Update public/API/API_GetComments.php --- public/API/API_GetComments.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index e2317d5074..042999fbe4 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -13,7 +13,7 @@ * c : count - number of entries to return (default: 100, max: 500) * * int Count number of comment records returned in the response -* int Total number of comment records the game/achievment/user actually has overall +* int Total number of comment records the game/achievement/user actually has overall * array Results * object [value] * int User username of the commenter From b86a4fbc25d45068f64730c6f3b1efbfb91dc4e6 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 09:47:00 -0400 Subject: [PATCH 09/28] adding validation to username --- public/API/API_GetComments.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index e2317d5074..beb9ab76d4 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -3,6 +3,8 @@ use App\Models\Achievement; use App\Models\Comment; use App\Models\User; +use App\Support\Rules\CtypeAlnum; +use Illuminate\Support\Facades\Validator; /* * API_GetComments - returns the comments associated to a game or achievement @@ -23,8 +25,8 @@ $input = Validator::validate(Arr::wrap(request()->query()), [ 'i' => ['sometimes', 'integer'], - 'u' => ['sometimes', 'string'], 't' => ['required', 'integer'], + 'u' => ['sometimes', 'min:2', 'max:20', new CtypeAlnum()], 'o' => ['sometimes', 'integer', 'min:0', 'nullable'], 'c' => ['sometimes', 'integer', 'min:1', 'max:500', 'nullable'], ]); @@ -39,6 +41,10 @@ if ($username) { $userId = User::firstWhere('User', $username); + if (!$userId) { + return []; + } + $comments = Comment::where('ArticleType', $commentType) ->offset($offset) ->limit($count) From ce062ab020249adabb56d0401c053832aee20389 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 09:47:05 -0400 Subject: [PATCH 10/28] fixing broken tests --- tests/Feature/Api/V1/GetCommentsTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Feature/Api/V1/GetCommentsTest.php b/tests/Feature/Api/V1/GetCommentsTest.php index 549f0ad719..38d1ba0f81 100644 --- a/tests/Feature/Api/V1/GetCommentsTest.php +++ b/tests/Feature/Api/V1/GetCommentsTest.php @@ -17,15 +17,15 @@ class API_GetCommentsTest extends TestCase public function testItValidates(): void { - $this->get($this->apiUrl('GetComments')) + $this->get($this->apiUrl('GetComments', ['u' => '1', 't' => 1])) ->assertJsonValidationErrors([ 'u', ]); } - public function testGetUserCompletionProgressUnknownUser(): void + public function testGetCommentsUnknownUser(): void { - $this->get($this->apiUrl('GetComments', ['u' => 'nonExistant'])) + $this->get($this->apiUrl('GetComments', ['u' => 'nonExistant', 't' => 3])) ->assertSuccessful() ->assertJson([]); } From 50cc249eac870cef7d669d32618870a7e179c59f Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 11:19:12 -0400 Subject: [PATCH 11/28] initial banned policy changes --- app/Policies/CommentPolicy.php | 3 +- public/API/API_GetComments.php | 14 ++++-- tests/Feature/Api/V1/GetCommentsTest.php | 63 ++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/app/Policies/CommentPolicy.php b/app/Policies/CommentPolicy.php index afa7b5a072..182565399b 100644 --- a/app/Policies/CommentPolicy.php +++ b/app/Policies/CommentPolicy.php @@ -26,7 +26,8 @@ public function manage(User $user): bool public function view(?User $user, Comment $comment): bool { - return true; + + return $user->isNotBanned(); } public function create(User $user, ?Model $commentable = null, ?int $articleType = null): bool diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index 8286e292c7..d8603baacb 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -5,6 +5,7 @@ use App\Models\User; use App\Support\Rules\CtypeAlnum; use Illuminate\Support\Facades\Validator; +use App\Policies\CommentPolicy; /* * API_GetComments - returns the comments associated to a game or achievement @@ -39,16 +40,16 @@ $commentType = (int) request()->query('t'); if ($username) { - $userId = User::firstWhere('User', $username); + $user = User::firstWhere('User', $username); - if (!$userId) { + if (!$user || !$user->UserWallActive) { return []; } $comments = Comment::where('ArticleType', $commentType) ->offset($offset) ->limit($count) - ->where('ArticleID', $userId->ID) + ->where('ArticleID', $user->ID) ->get(); } else { $comments = Comment::where('ArticleType', $commentType) @@ -60,10 +61,15 @@ $results = []; +$policy = new CommentPolicy(); + if (!empty($comments)) { foreach ($comments as $nextComment) { $user = User::firstWhere('ID', $nextComment['user_id']); - if ($user) { + + + + if ($user && $policy->view(request()->user(), $nextComment)) { $commentData = [ 'User' => $user->username, 'Submitted' => $nextComment->Submitted, diff --git a/tests/Feature/Api/V1/GetCommentsTest.php b/tests/Feature/Api/V1/GetCommentsTest.php index 38d1ba0f81..876f52d3c2 100644 --- a/tests/Feature/Api/V1/GetCommentsTest.php +++ b/tests/Feature/Api/V1/GetCommentsTest.php @@ -6,6 +6,7 @@ use App\Models\System; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Carbon; use Illuminate\Foundation\Testing\WithFaker; use Tests\Feature\Api\V1\BootstrapsApiV1; use Tests\TestCase; @@ -37,6 +38,9 @@ public function testGetCommentsForAchievement(): void $game = Game::factory()->create(['ConsoleID' => $system->ID]); $user1 = User::factory()->create(); $user2 = User::factory()->create(); + $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); + + debug_to_console($bannedUser); $achievement = Achievement::factory()->create(['GameID' => $game->ID, 'user_id' => $user1->ID]); $comment1 = Comment::factory()->create([ @@ -51,6 +55,12 @@ public function testGetCommentsForAchievement(): void 'user_id' => $user2->ID, 'Payload' => 'I agree, this is awesome!', ]); + $comment3 = Comment::factory()->create([ + 'ArticleID' => $achievement->ID, + 'ArticleType' => 2, + 'user_id' => $bannedUser->ID, + 'Payload' => 'This comment is from a banned user!', + ]); // Act $response = $this->get($this->apiUrl('GetComments', ['i' => $achievement->ID, 't' => 2])) @@ -83,6 +93,8 @@ public function testGetCommentsForGame(): void $game = Game::factory()->create(['ConsoleID' => $system->ID]); $user1 = User::factory()->create(); $user2 = User::factory()->create(); + $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); + $comment1 = Comment::factory()->create([ 'ArticleID' => $game->ID, 'ArticleType' => 1, @@ -95,6 +107,12 @@ public function testGetCommentsForGame(): void 'user_id' => $user2->ID, 'Payload' => 'I agree, this is awesome!', ]); + $comment3 = Comment::factory()->create([ + 'ArticleID' => $achievement->ID, + 'ArticleType' => 2, + 'user_id' => $bannedUser->ID, + 'Payload' => 'This comment is from a banned user!', + ]); // Act $response = $this->get($this->apiUrl('GetComments', ['i' => $game->ID, 't' => 1])) @@ -125,6 +143,8 @@ public function testGetCommentsForUser(): void // Arrange $user = User::factory()->create(); $user2 = User::factory()->create(); + $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); + $comment1 = Comment::factory()->create([ 'ArticleID' => $user->ID, 'ArticleType' => 3, @@ -137,6 +157,12 @@ public function testGetCommentsForUser(): void 'user_id' => $user2->ID, 'Payload' => 'This is my second comment.', ]); + $comment3 = Comment::factory()->create([ + 'ArticleID' => $achievement->ID, + 'ArticleType' => 2, + 'user_id' => $bannedUser->ID, + 'Payload' => 'This comment is from a banned user!', + ]); // Act $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) @@ -161,4 +187,41 @@ public function testGetCommentsForUser(): void $this->assertEquals($user2->User, $response->json('Results.1.User')); $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); } + + public function testGetCommentsForUserWithDisabledWall(): void + { + // Arrange + $user = User::factory()->create(['UserWallActive' => false]); + $user2 = User::factory()->create(); + $comment1 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 3, + 'user_id' => $user2->ID, + 'Payload' => 'This is my first comment.', + ]); + $comment2 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 3, + 'user_id' => $user2->ID, + 'Payload' => 'This is my second comment.', + ]); + + debug_to_console($user); + + // Act + $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) + ->assertSuccessful(); + + // Assert + $response->assertStatus(200); + $response->assertJsonStructure([]); + } } + +function debug_to_console($data) { + $output = $data; + if (is_array($output)) + $output = implode(',', $output); + + echo ""; +} \ No newline at end of file From 699b221ac28b53e9fc3b48d5a499a04e83087cdc Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 10 Jul 2024 14:35:49 -0400 Subject: [PATCH 12/28] pint --- public/API/API_GetComments.php | 4 +--- tests/Feature/Api/V1/GetCommentsTest.php | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index d8603baacb..3e2d33005f 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -3,9 +3,9 @@ use App\Models\Achievement; use App\Models\Comment; use App\Models\User; +use App\Policies\CommentPolicy; use App\Support\Rules\CtypeAlnum; use Illuminate\Support\Facades\Validator; -use App\Policies\CommentPolicy; /* * API_GetComments - returns the comments associated to a game or achievement @@ -67,8 +67,6 @@ foreach ($comments as $nextComment) { $user = User::firstWhere('ID', $nextComment['user_id']); - - if ($user && $policy->view(request()->user(), $nextComment)) { $commentData = [ 'User' => $user->username, diff --git a/tests/Feature/Api/V1/GetCommentsTest.php b/tests/Feature/Api/V1/GetCommentsTest.php index 876f52d3c2..48297376e2 100644 --- a/tests/Feature/Api/V1/GetCommentsTest.php +++ b/tests/Feature/Api/V1/GetCommentsTest.php @@ -6,8 +6,8 @@ use App\Models\System; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Carbon; use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Support\Carbon; use Tests\Feature\Api\V1\BootstrapsApiV1; use Tests\TestCase; @@ -144,7 +144,7 @@ public function testGetCommentsForUser(): void $user = User::factory()->create(); $user2 = User::factory()->create(); $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); - + $comment1 = Comment::factory()->create([ 'ArticleID' => $user->ID, 'ArticleType' => 3, @@ -218,10 +218,12 @@ public function testGetCommentsForUserWithDisabledWall(): void } } -function debug_to_console($data) { +function debug_to_console($data) +{ $output = $data; - if (is_array($output)) + if (is_array($output)) { $output = implode(',', $output); + } echo ""; -} \ No newline at end of file +} From 86676092a4d651847053caa522b90006ae225ae6 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 31 Jul 2024 11:05:00 -0400 Subject: [PATCH 13/28] removing blank line --- app/Policies/CommentPolicy.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Policies/CommentPolicy.php b/app/Policies/CommentPolicy.php index 182565399b..3881cf0419 100644 --- a/app/Policies/CommentPolicy.php +++ b/app/Policies/CommentPolicy.php @@ -26,7 +26,6 @@ public function manage(User $user): bool public function view(?User $user, Comment $comment): bool { - return $user->isNotBanned(); } From 1fa0bcdbd6eacf0ec0d46c933583129a0f59615a Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 31 Jul 2024 11:14:12 -0400 Subject: [PATCH 14/28] removing debug func and refactoring tests to have full expected JSON --- tests/Feature/Api/V1/GetCommentsTest.php | 94 ++++++++++-------------- 1 file changed, 40 insertions(+), 54 deletions(-) diff --git a/tests/Feature/Api/V1/GetCommentsTest.php b/tests/Feature/Api/V1/GetCommentsTest.php index 48297376e2..8a8181ff6f 100644 --- a/tests/Feature/Api/V1/GetCommentsTest.php +++ b/tests/Feature/Api/V1/GetCommentsTest.php @@ -11,7 +11,7 @@ use Tests\Feature\Api\V1\BootstrapsApiV1; use Tests\TestCase; -class API_GetCommentsTest extends TestCase +class GetCommentsTest extends TestCase { use RefreshDatabase; use WithFaker; use BootstrapsApiV1; @@ -38,9 +38,7 @@ public function testGetCommentsForAchievement(): void $game = Game::factory()->create(['ConsoleID' => $system->ID]); $user1 = User::factory()->create(); $user2 = User::factory()->create(); - $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); - - debug_to_console($bannedUser); + $bannedUser = User::factory()->create(['ID' => 309, 'banned_at' => Carbon::now()->subDay()]); $achievement = Achievement::factory()->create(['GameID' => $game->ID, 'user_id' => $user1->ID]); $comment1 = Comment::factory()->create([ @@ -68,22 +66,22 @@ public function testGetCommentsForAchievement(): void // Assert $response->assertStatus(200); - $response->assertJsonStructure([ - 'Count', - 'Total', + $response->assertJson([ + 'Count' => 2, + 'Total' => 2, 'Results' => [ - '*' => [ - 'User', - 'Submitted', - 'CommentText', + [ + 'User' => $user1->User, + 'Submitted' => $comment1->Submitted->toISOString(), + 'CommentText' => $comment1->Payload, + ], + [ + 'User' => $user2->User, + 'Submitted' => $comment2->Submitted->toISOString(), + 'CommentText' => $comment2->Payload, ], ], ]); - $this->assertCount(2, $response->json('Results')); - $this->assertEquals($user1->User, $response->json('Results.0.User')); - $this->assertEquals($comment1->Payload, $response->json('Results.0.CommentText')); - $this->assertEquals($user2->User, $response->json('Results.1.User')); - $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); } public function testGetCommentsForGame(): void @@ -108,7 +106,7 @@ public function testGetCommentsForGame(): void 'Payload' => 'I agree, this is awesome!', ]); $comment3 = Comment::factory()->create([ - 'ArticleID' => $achievement->ID, + 'ArticleID' => $game->ID, 'ArticleType' => 2, 'user_id' => $bannedUser->ID, 'Payload' => 'This comment is from a banned user!', @@ -120,22 +118,22 @@ public function testGetCommentsForGame(): void // Assert $response->assertStatus(200); - $response->assertJsonStructure([ - 'Count', - 'Total', + $response->assertJson([ + 'Count' => 2, + 'Total' => 2, 'Results' => [ - '*' => [ - 'User', - 'Submitted', - 'CommentText', + [ + 'User' => $user1->User, + 'Submitted' => $comment1->Submitted->toISOString(), + 'CommentText' => $comment1->Payload, + ], + [ + 'User' => $user2->User, + 'Submitted' => $comment2->Submitted->toISOString(), + 'CommentText' => $comment2->Payload, ], ], ]); - $this->assertCount(2, $response->json('Results')); - $this->assertEquals($user1->username, $response->json('Results.0.User')); - $this->assertEquals($comment1->Payload, $response->json('Results.0.CommentText')); - $this->assertEquals($user2->username, $response->json('Results.1.User')); - $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); } public function testGetCommentsForUser(): void @@ -158,7 +156,7 @@ public function testGetCommentsForUser(): void 'Payload' => 'This is my second comment.', ]); $comment3 = Comment::factory()->create([ - 'ArticleID' => $achievement->ID, + 'ArticleID' => $user->ID, 'ArticleType' => 2, 'user_id' => $bannedUser->ID, 'Payload' => 'This comment is from a banned user!', @@ -170,22 +168,22 @@ public function testGetCommentsForUser(): void // Assert $response->assertStatus(200); - $response->assertJsonStructure([ - 'Count', - 'Total', + $response->assertJson([ + 'Count' => 2, + 'Total' => 2, 'Results' => [ - '*' => [ - 'User', - 'Submitted', - 'CommentText', + [ + 'User' => $user2->User, + 'Submitted' => $comment1->Submitted->toISOString(), + 'CommentText' => $comment1->Payload, + ], + [ + 'User' => $user2->User, + 'Submitted' => $comment2->Submitted->toISOString(), + 'CommentText' => $comment2->Payload, ], ], ]); - $this->assertCount(2, $response->json('Results')); - $this->assertEquals($user2->User, $response->json('Results.0.User')); - $this->assertEquals($comment1->Payload, $response->json('Results.0.CommentText')); - $this->assertEquals($user2->User, $response->json('Results.1.User')); - $this->assertEquals($comment2->Payload, $response->json('Results.1.CommentText')); } public function testGetCommentsForUserWithDisabledWall(): void @@ -206,8 +204,6 @@ public function testGetCommentsForUserWithDisabledWall(): void 'Payload' => 'This is my second comment.', ]); - debug_to_console($user); - // Act $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) ->assertSuccessful(); @@ -217,13 +213,3 @@ public function testGetCommentsForUserWithDisabledWall(): void $response->assertJsonStructure([]); } } - -function debug_to_console($data) -{ - $output = $data; - if (is_array($output)) { - $output = implode(',', $output); - } - - echo ""; -} From 67977874067297586e4992a2d3dc7e5b77c77039 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 31 Jul 2024 11:14:43 -0400 Subject: [PATCH 15/28] refactoring endpoint for efficiency --- public/API/API_GetComments.php | 59 ++++++++++++++++------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index 3e2d33005f..099329b2ea 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -39,48 +39,45 @@ $username = (string) request()->query('u'); $commentType = (int) request()->query('t'); +$user = null; + if ($username) { $user = User::firstWhere('User', $username); - if (!$user || !$user->UserWallActive) { - return []; + return response()->json(['Count' => 0, 'Total' => 0, 'Results' => []]); } - - $comments = Comment::where('ArticleType', $commentType) - ->offset($offset) - ->limit($count) - ->where('ArticleID', $user->ID) - ->get(); -} else { - $comments = Comment::where('ArticleType', $commentType) - ->offset($offset) - ->limit($count) - ->where('ArticleID', $gameOrAchievementId) - ->get(); } -$results = []; +$articleId = $user ? $user->ID : $gameOrAchievementId; -$policy = new CommentPolicy(); +$comments = Comment::where('ArticleType', $commentType) + ->where('ArticleID', $articleId) + ->offset($offset) + ->limit($count) + ->with('user') + ->get(); -if (!empty($comments)) { - foreach ($comments as $nextComment) { - $user = User::firstWhere('ID', $nextComment['user_id']); +$totalComments = Comment::where('ArticleType', $commentType) + ->where('ArticleID', $articleId) + ->whereHas('user', function ($query) { + $query->whereNull('banned_at'); + }) + ->count(); - if ($user && $policy->view(request()->user(), $nextComment)) { - $commentData = [ - 'User' => $user->username, - 'Submitted' => $nextComment->Submitted, - 'CommentText' => $nextComment->Payload, - ]; +$policy = new CommentPolicy(); - array_push($results, $commentData); - } - } -} +$results = $comments->filter(function ($nextComment) use ($policy) { + return $policy->view($nextComment->user, $nextComment); +})->map(function ($nextComment) { + return [ + 'User' => $nextComment->user->username, + 'Submitted' => $nextComment->Submitted, + 'CommentText' => $nextComment->Payload, + ]; +}); return response()->json([ - 'Count' => count($results), - 'Total' => count($comments), + 'Count' => $results->count(), + 'Total' => $totalComments, 'Results' => $results, ]); From cc29d382e5631735bbe2df7fa8292c3a4925f787 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 7 Aug 2024 10:09:31 -0400 Subject: [PATCH 16/28] adding newFactory func to Comment model --- app/Models/Comment.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Models/Comment.php b/app/Models/Comment.php index 2ce4157e3b..42a940dea0 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -7,6 +7,7 @@ use App\Support\Database\Eloquent\BaseModel; use Exception; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Database\Factories\CommentFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; @@ -98,4 +99,9 @@ public function user(): BelongsTo { return $this->belongsTo(User::class, 'user_id', 'ID')->withDefault(['username' => 'Deleted User']); } + + protected static function newFactory(): CommentFactory + { + return CommentFactory::new(); + } } From f638a7e80ed35ab323ffa17c4d54be653f9be26e Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 7 Aug 2024 10:14:41 -0400 Subject: [PATCH 17/28] renaming comments test --- tests/Feature/Api/V1/GetCommentsTest.php | 215 ----------------------- 1 file changed, 215 deletions(-) delete mode 100644 tests/Feature/Api/V1/GetCommentsTest.php diff --git a/tests/Feature/Api/V1/GetCommentsTest.php b/tests/Feature/Api/V1/GetCommentsTest.php deleted file mode 100644 index 8a8181ff6f..0000000000 --- a/tests/Feature/Api/V1/GetCommentsTest.php +++ /dev/null @@ -1,215 +0,0 @@ -get($this->apiUrl('GetComments', ['u' => '1', 't' => 1])) - ->assertJsonValidationErrors([ - 'u', - ]); - } - - public function testGetCommentsUnknownUser(): void - { - $this->get($this->apiUrl('GetComments', ['u' => 'nonExistant', 't' => 3])) - ->assertSuccessful() - ->assertJson([]); - } - - public function testGetCommentsForAchievement(): void - { - // Arrange - $system = System::factory()->create(); - $game = Game::factory()->create(['ConsoleID' => $system->ID]); - $user1 = User::factory()->create(); - $user2 = User::factory()->create(); - $bannedUser = User::factory()->create(['ID' => 309, 'banned_at' => Carbon::now()->subDay()]); - - $achievement = Achievement::factory()->create(['GameID' => $game->ID, 'user_id' => $user1->ID]); - $comment1 = Comment::factory()->create([ - 'ArticleID' => $achievement->ID, - 'ArticleType' => 2, - 'user_id' => $user1->ID, - 'Payload' => 'This is a great achievement!', - ]); - $comment2 = Comment::factory()->create([ - 'ArticleID' => $achievement->ID, - 'ArticleType' => 2, - 'user_id' => $user2->ID, - 'Payload' => 'I agree, this is awesome!', - ]); - $comment3 = Comment::factory()->create([ - 'ArticleID' => $achievement->ID, - 'ArticleType' => 2, - 'user_id' => $bannedUser->ID, - 'Payload' => 'This comment is from a banned user!', - ]); - - // Act - $response = $this->get($this->apiUrl('GetComments', ['i' => $achievement->ID, 't' => 2])) - ->assertSuccessful(); - - // Assert - $response->assertStatus(200); - $response->assertJson([ - 'Count' => 2, - 'Total' => 2, - 'Results' => [ - [ - 'User' => $user1->User, - 'Submitted' => $comment1->Submitted->toISOString(), - 'CommentText' => $comment1->Payload, - ], - [ - 'User' => $user2->User, - 'Submitted' => $comment2->Submitted->toISOString(), - 'CommentText' => $comment2->Payload, - ], - ], - ]); - } - - public function testGetCommentsForGame(): void - { - // Arrange - $system = System::factory()->create(); - $game = Game::factory()->create(['ConsoleID' => $system->ID]); - $user1 = User::factory()->create(); - $user2 = User::factory()->create(); - $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); - - $comment1 = Comment::factory()->create([ - 'ArticleID' => $game->ID, - 'ArticleType' => 1, - 'user_id' => $user1->ID, - 'Payload' => 'This is a great achievement!', - ]); - $comment2 = Comment::factory()->create([ - 'ArticleID' => $game->ID, - 'ArticleType' => 1, - 'user_id' => $user2->ID, - 'Payload' => 'I agree, this is awesome!', - ]); - $comment3 = Comment::factory()->create([ - 'ArticleID' => $game->ID, - 'ArticleType' => 2, - 'user_id' => $bannedUser->ID, - 'Payload' => 'This comment is from a banned user!', - ]); - - // Act - $response = $this->get($this->apiUrl('GetComments', ['i' => $game->ID, 't' => 1])) - ->assertSuccessful(); - - // Assert - $response->assertStatus(200); - $response->assertJson([ - 'Count' => 2, - 'Total' => 2, - 'Results' => [ - [ - 'User' => $user1->User, - 'Submitted' => $comment1->Submitted->toISOString(), - 'CommentText' => $comment1->Payload, - ], - [ - 'User' => $user2->User, - 'Submitted' => $comment2->Submitted->toISOString(), - 'CommentText' => $comment2->Payload, - ], - ], - ]); - } - - public function testGetCommentsForUser(): void - { - // Arrange - $user = User::factory()->create(); - $user2 = User::factory()->create(); - $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); - - $comment1 = Comment::factory()->create([ - 'ArticleID' => $user->ID, - 'ArticleType' => 3, - 'user_id' => $user2->ID, - 'Payload' => 'This is my first comment.', - ]); - $comment2 = Comment::factory()->create([ - 'ArticleID' => $user->ID, - 'ArticleType' => 3, - 'user_id' => $user2->ID, - 'Payload' => 'This is my second comment.', - ]); - $comment3 = Comment::factory()->create([ - 'ArticleID' => $user->ID, - 'ArticleType' => 2, - 'user_id' => $bannedUser->ID, - 'Payload' => 'This comment is from a banned user!', - ]); - - // Act - $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) - ->assertSuccessful(); - - // Assert - $response->assertStatus(200); - $response->assertJson([ - 'Count' => 2, - 'Total' => 2, - 'Results' => [ - [ - 'User' => $user2->User, - 'Submitted' => $comment1->Submitted->toISOString(), - 'CommentText' => $comment1->Payload, - ], - [ - 'User' => $user2->User, - 'Submitted' => $comment2->Submitted->toISOString(), - 'CommentText' => $comment2->Payload, - ], - ], - ]); - } - - public function testGetCommentsForUserWithDisabledWall(): void - { - // Arrange - $user = User::factory()->create(['UserWallActive' => false]); - $user2 = User::factory()->create(); - $comment1 = Comment::factory()->create([ - 'ArticleID' => $user->ID, - 'ArticleType' => 3, - 'user_id' => $user2->ID, - 'Payload' => 'This is my first comment.', - ]); - $comment2 = Comment::factory()->create([ - 'ArticleID' => $user->ID, - 'ArticleType' => 3, - 'user_id' => $user2->ID, - 'Payload' => 'This is my second comment.', - ]); - - // Act - $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) - ->assertSuccessful(); - - // Assert - $response->assertStatus(200); - $response->assertJsonStructure([]); - } -} From 6d273616f8d9138c128e68e6bcfc76a0a434e19a Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 7 Aug 2024 10:15:19 -0400 Subject: [PATCH 18/28] add random user, remove commentableId/Type, add ArticleID/Type to CommmentFactory --- database/factories/CommentFactory.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/database/factories/CommentFactory.php b/database/factories/CommentFactory.php index 1a0346de06..1acdffc967 100644 --- a/database/factories/CommentFactory.php +++ b/database/factories/CommentFactory.php @@ -5,6 +5,7 @@ namespace Database\Factories; use App\Models\Comment; +use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -16,17 +17,15 @@ class CommentFactory extends Factory public function definition(): array { + $user = User::inRandomOrder()->first(); + return [ 'Payload' => $this->faker->paragraph, 'Submitted' => $this->faker->dateTimeBetween('-1 year', 'now'), 'Edited' => $this->faker->dateTimeBetween('now', '+1 year'), - 'user_id' => 1, - 'commentable_id' => $this->faker->numberBetween(1, 100), - 'commentable_type' => $this->faker->randomElement([ - 'App\Models\Article', - 'App\Models\Post', - 'App\Models\Product', - ]), + 'user_id' => $user->ID, + 'ArticleID' => $this->faker->numberBetween(1, 100), + 'ArticleType' => $this->faker->numberBetween(1, 3), ]; } } From 38340b53c5e78d2bc8343d16b12b76815211be8e Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 7 Aug 2024 10:15:28 -0400 Subject: [PATCH 19/28] rename comments test --- tests/Feature/Api/V1/CommentsTest.php | 215 ++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 tests/Feature/Api/V1/CommentsTest.php diff --git a/tests/Feature/Api/V1/CommentsTest.php b/tests/Feature/Api/V1/CommentsTest.php new file mode 100644 index 0000000000..e4192fa614 --- /dev/null +++ b/tests/Feature/Api/V1/CommentsTest.php @@ -0,0 +1,215 @@ +get($this->apiUrl('GetComments', ['u' => '1', 't' => 1])) + ->assertJsonValidationErrors([ + 'u', + ]); + } + + public function testGetCommentsUnknownUser(): void + { + $this->get($this->apiUrl('GetComments', ['u' => 'nonExistant', 't' => 3])) + ->assertSuccessful() + ->assertJson([]); + } + + public function testGetCommentsForAchievement(): void + { + // Arrange + $system = System::factory()->create(); + $game = Game::factory()->create(['ConsoleID' => $system->ID]); + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $bannedUser = User::factory()->create(['ID' => 309, 'banned_at' => Carbon::now()->subDay()]); + + $achievement = Achievement::factory()->create(['GameID' => $game->ID, 'user_id' => $user1->ID]); + $comment1 = Comment::factory()->create([ + 'ArticleID' => $achievement->ID, + 'ArticleType' => 2, + 'user_id' => $user1->ID, + 'Payload' => 'This is a great achievement!', + ]); + $comment2 = Comment::factory()->create([ + 'ArticleID' => $achievement->ID, + 'ArticleType' => 2, + 'user_id' => $user2->ID, + 'Payload' => 'I agree, this is awesome!', + ]); + $comment3 = Comment::factory()->create([ + 'ArticleID' => $achievement->ID, + 'ArticleType' => 2, + 'user_id' => $bannedUser->ID, + 'Payload' => 'This comment is from a banned user!', + ]); + + // Act + $response = $this->get($this->apiUrl('GetComments', ['i' => $achievement->ID, 't' => 2])) + ->assertSuccessful(); + + // Assert + $response->assertStatus(200); + $response->assertJson([ + 'Count' => 2, + 'Total' => 2, + 'Results' => [ + [ + 'User' => $user1->User, + 'Submitted' => $comment1->Submitted->toISOString(), + 'CommentText' => $comment1->Payload, + ], + [ + 'User' => $user2->User, + 'Submitted' => $comment2->Submitted->toISOString(), + 'CommentText' => $comment2->Payload, + ], + ], + ]); + } + + public function testGetCommentsForGame(): void + { + // Arrange + $system = System::factory()->create(); + $game = Game::factory()->create(['ConsoleID' => $system->ID]); + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); + + $comment1 = Comment::factory()->create([ + 'ArticleID' => $game->ID, + 'ArticleType' => 1, + 'user_id' => $user1->ID, + 'Payload' => 'This is a great achievement!', + ]); + $comment2 = Comment::factory()->create([ + 'ArticleID' => $game->ID, + 'ArticleType' => 1, + 'user_id' => $user2->ID, + 'Payload' => 'I agree, this is awesome!', + ]); + $comment3 = Comment::factory()->create([ + 'ArticleID' => $game->ID, + 'ArticleType' => 2, + 'user_id' => $bannedUser->ID, + 'Payload' => 'This comment is from a banned user!', + ]); + + // Act + $response = $this->get($this->apiUrl('GetComments', ['i' => $game->ID, 't' => 1])) + ->assertSuccessful(); + + // Assert + $response->assertStatus(200); + $response->assertJson([ + 'Count' => 2, + 'Total' => 2, + 'Results' => [ + [ + 'User' => $user1->User, + 'Submitted' => $comment1->Submitted->toISOString(), + 'CommentText' => $comment1->Payload, + ], + [ + 'User' => $user2->User, + 'Submitted' => $comment2->Submitted->toISOString(), + 'CommentText' => $comment2->Payload, + ], + ], + ]); + } + + public function testGetCommentsForUser(): void + { + // Arrange + $user = User::factory()->create(); + $user2 = User::factory()->create(); + $bannedUser = User::factory()->create(['banned_at' => Carbon::now()]); + + $comment1 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 3, + 'user_id' => $user2->ID, + 'Payload' => 'This is my first comment.', + ]); + $comment2 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 3, + 'user_id' => $user2->ID, + 'Payload' => 'This is my second comment.', + ]); + $comment3 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 2, + 'user_id' => $bannedUser->ID, + 'Payload' => 'This comment is from a banned user!', + ]); + + // Act + $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) + ->assertSuccessful(); + + // Assert + $response->assertStatus(200); + $response->assertJson([ + 'Count' => 2, + 'Total' => 2, + 'Results' => [ + [ + 'User' => $user2->User, + 'Submitted' => $comment1->Submitted->toISOString(), + 'CommentText' => $comment1->Payload, + ], + [ + 'User' => $user2->User, + 'Submitted' => $comment2->Submitted->toISOString(), + 'CommentText' => $comment2->Payload, + ], + ], + ]); + } + + public function testGetCommentsForUserWithDisabledWall(): void + { + // Arrange + $user = User::factory()->create(['UserWallActive' => false]); + $user2 = User::factory()->create(); + $comment1 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 3, + 'user_id' => $user2->ID, + 'Payload' => 'This is my first comment.', + ]); + $comment2 = Comment::factory()->create([ + 'ArticleID' => $user->ID, + 'ArticleType' => 3, + 'user_id' => $user2->ID, + 'Payload' => 'This is my second comment.', + ]); + + // Act + $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) + ->assertSuccessful(); + + // Assert + $response->assertStatus(200); + $response->assertJsonStructure([]); + } +} From a5c528095b09225a34493bff24e53128a47fe914 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 7 Aug 2024 10:16:24 -0400 Subject: [PATCH 20/28] pint --- app/Models/Comment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Comment.php b/app/Models/Comment.php index 42a940dea0..14789b40b6 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -5,9 +5,9 @@ namespace App\Models; use App\Support\Database\Eloquent\BaseModel; +use Database\Factories\CommentFactory; use Exception; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Database\Factories\CommentFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; From f837d4bb6cc2dcb234ced4bee44ee1b575adf7ca Mon Sep 17 00:00:00 2001 From: iOSLife Date: Tue, 13 Aug 2024 09:35:12 -0400 Subject: [PATCH 21/28] Add `withTrashed()` to Comment queries to prevent issues with deleted Users --- public/API/API_GetComments.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index 099329b2ea..a4fec88180 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -50,14 +50,16 @@ $articleId = $user ? $user->ID : $gameOrAchievementId; -$comments = Comment::where('ArticleType', $commentType) +$comments = Comment::withTrashed() + ->where('ArticleType', $commentType) ->where('ArticleID', $articleId) ->offset($offset) ->limit($count) ->with('user') ->get(); -$totalComments = Comment::where('ArticleType', $commentType) +$totalComments = Comment::withTrashed() + ->where('ArticleType', $commentType) ->where('ArticleID', $articleId) ->whereHas('user', function ($query) { $query->whereNull('banned_at'); From 452ee8f01ab505bb592189fd858bb27076bcb545 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Wed, 21 Aug 2024 13:40:19 -0400 Subject: [PATCH 22/28] Update GetComments to return 404 when invalid username or user wall is not active. Prevent soft deleted comments from returning. --- public/API/API_GetComments.php | 4 +++- tests/Feature/Api/V1/CommentsTest.php | 16 ++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index a4fec88180..09c6318131 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -44,7 +44,7 @@ if ($username) { $user = User::firstWhere('User', $username); if (!$user || !$user->UserWallActive) { - return response()->json(['Count' => 0, 'Total' => 0, 'Results' => []]); + return response()->json([], 404); } } @@ -53,6 +53,7 @@ $comments = Comment::withTrashed() ->where('ArticleType', $commentType) ->where('ArticleID', $articleId) + ->whereNull('deleted_at') ->offset($offset) ->limit($count) ->with('user') @@ -61,6 +62,7 @@ $totalComments = Comment::withTrashed() ->where('ArticleType', $commentType) ->where('ArticleID', $articleId) + ->whereNull('deleted_at') ->whereHas('user', function ($query) { $query->whereNull('banned_at'); }) diff --git a/tests/Feature/Api/V1/CommentsTest.php b/tests/Feature/Api/V1/CommentsTest.php index e4192fa614..aebcc232dc 100644 --- a/tests/Feature/Api/V1/CommentsTest.php +++ b/tests/Feature/Api/V1/CommentsTest.php @@ -27,7 +27,7 @@ public function testItValidates(): void public function testGetCommentsUnknownUser(): void { $this->get($this->apiUrl('GetComments', ['u' => 'nonExistant', 't' => 3])) - ->assertSuccessful() + ->assertNotFound() ->assertJson([]); } @@ -111,6 +111,13 @@ public function testGetCommentsForGame(): void 'user_id' => $bannedUser->ID, 'Payload' => 'This comment is from a banned user!', ]); + $deletedComment = Comment::factory()->create([ + 'ArticleID' => $game->ID, + 'ArticleType' => 2, + 'user_id' => $user1->ID, + 'Payload' => 'This comment has been deleted!', + 'deleted_at' => Carbon::now(), + ]); // Act $response = $this->get($this->apiUrl('GetComments', ['i' => $game->ID, 't' => 1])) @@ -206,10 +213,7 @@ public function testGetCommentsForUserWithDisabledWall(): void // Act $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) - ->assertSuccessful(); - - // Assert - $response->assertStatus(200); - $response->assertJsonStructure([]); + ->assertNotFound() + ->assertJson([]); } } From 65f58688d704fa0b698716f9c72f6fe84fe5f215 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Sat, 24 Aug 2024 21:04:45 -0400 Subject: [PATCH 23/28] Remove duplicate newFactory method in Comment --- app/Models/Comment.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/Models/Comment.php b/app/Models/Comment.php index c62d784984..9088fcb259 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -104,9 +104,4 @@ public function user(): BelongsTo { return $this->belongsTo(User::class, 'user_id', 'ID')->withDefault(['username' => 'Deleted User']); } - - protected static function newFactory(): CommentFactory - { - return CommentFactory::new(); - } } From c4d8ca74dc343ca5f24538b62780b13751767010 Mon Sep 17 00:00:00 2001 From: iOSLife Date: Sat, 24 Aug 2024 21:06:27 -0400 Subject: [PATCH 24/28] Run pint --- database/factories/CommentFactory.php | 1 - 1 file changed, 1 deletion(-) diff --git a/database/factories/CommentFactory.php b/database/factories/CommentFactory.php index 5b09e73907..e0213a2713 100644 --- a/database/factories/CommentFactory.php +++ b/database/factories/CommentFactory.php @@ -4,7 +4,6 @@ namespace Database\Factories; -use App\Community\Enums\ArticleType; use App\Models\Comment; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; From b0e1a111be26e32986f91b9520f2e99da5906f8c Mon Sep 17 00:00:00 2001 From: iOSLife Date: Thu, 5 Sep 2024 12:37:28 -0400 Subject: [PATCH 25/28] Refactor Comment endpoint to no longer need 'u' param and handle it as part of the 'i' param --- public/API/API_GetComments.php | 56 ++++++++++++++++++++------- tests/Feature/Api/V1/CommentsTest.php | 30 ++++++++++---- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index 09c6318131..1c319e0527 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -4,13 +4,13 @@ use App\Models\Comment; use App\Models\User; use App\Policies\CommentPolicy; -use App\Support\Rules\CtypeAlnum; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; /* * API_GetComments - returns the comments associated to a game or achievement -* i : game or achievement id -* u : username +* i : game or achievement id or username * t : 1 = game, 2 = achievement, 3 = user * o : offset - number of entries to skip (default: 0) * c : count - number of entries to return (default: 100, max: 500) @@ -24,26 +24,49 @@ * string CommentText text of the comment */ -$input = Validator::validate(Arr::wrap(request()->query()), [ - 'i' => ['sometimes', 'integer'], - 't' => ['required', 'integer'], - 'u' => ['sometimes', 'min:2', 'max:20', new CtypeAlnum()], +$query = request()->query(); + +$inputIsGameOrAchievement = function () use ($query) { + return isset($query['i']) && is_numeric($query['i']) && intval($query['i']) == $query['i']; +}; + +$rules = [ + 'i' => [ + 'required', + Rule::when(isset($query['t']) && $query['t'] === '3', 'string'), + Rule::when(isset($query['t']) && in_array($query['t'], [1, 2]), 'integer'), + ], + 't' => [ + Rule::requiredIf($inputIsGameOrAchievement()), + 'integer', + ], 'o' => ['sometimes', 'integer', 'min:0', 'nullable'], 'c' => ['sometimes', 'integer', 'min:1', 'max:500', 'nullable'], -]); +]; + +$input = Validator::validate(Arr::wrap($query), $rules); $offset = $input['o'] ?? 0; $count = $input['c'] ?? 100; -$gameOrAchievementId = (int) request()->query('i'); -$username = (string) request()->query('u'); -$commentType = (int) request()->query('t'); +$username = null; +$gameOrAchievementId = 0; +$commentType = 0; + +if ($inputIsGameOrAchievement()) { + $gameOrAchievementId = $query['i']; + $commentType = $query['t']; +} else { + $username = $query['i']; + $commentType = 3; +} $user = null; if ($username) { $user = User::firstWhere('User', $username); - if (!$user || !$user->UserWallActive) { + + if (!$user || !$user->UserWallActive || $user->banned_at || Auth::user()->banned_at) { return response()->json([], 404); } } @@ -51,12 +74,15 @@ $articleId = $user ? $user->ID : $gameOrAchievementId; $comments = Comment::withTrashed() + ->with('user') ->where('ArticleType', $commentType) ->where('ArticleID', $articleId) ->whereNull('deleted_at') + ->whereHas('user', function ($query) { + $query->whereNull('banned_at'); + }) ->offset($offset) ->limit($count) - ->with('user') ->get(); $totalComments = Comment::withTrashed() @@ -71,7 +97,9 @@ $policy = new CommentPolicy(); $results = $comments->filter(function ($nextComment) use ($policy) { - return $policy->view($nextComment->user, $nextComment); + $user = Auth::user() instanceof User ? Auth::user() : null; + + return $policy->view($user, $nextComment); })->map(function ($nextComment) { return [ 'User' => $nextComment->user->username, diff --git a/tests/Feature/Api/V1/CommentsTest.php b/tests/Feature/Api/V1/CommentsTest.php index aebcc232dc..6ead80c04e 100644 --- a/tests/Feature/Api/V1/CommentsTest.php +++ b/tests/Feature/Api/V1/CommentsTest.php @@ -13,20 +13,36 @@ class CommentsTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; + use WithFaker; use BootstrapsApiV1; public function testItValidates(): void { - $this->get($this->apiUrl('GetComments', ['u' => '1', 't' => 1])) - ->assertJsonValidationErrors([ - 'u', + $this->get($this->apiUrl('GetComments', ['i' => '1', 't' => 3])) + ->assertJsonMissingValidationErrors([ + 'i', ]); + + $this->get($this->apiUrl('GetComments', ['i' => 1, 't' => 1])) + ->assertJsonMissingValidationErrors([ + 'i', + 't', + ]); + + $this->get($this->apiUrl('GetComments', ['i' => 'invalid', 't' => 2])) + ->assertJsonValidationErrors(['i']); + + $this->get($this->apiUrl('GetComments', ['i' => 'not-an-integer', 't' => 1])) + ->assertJsonValidationErrors(['i']); + + $this->get($this->apiUrl('GetComments', ['i' => null, 't' => 2])) + ->assertJsonValidationErrors(['i']); } public function testGetCommentsUnknownUser(): void { - $this->get($this->apiUrl('GetComments', ['u' => 'nonExistant', 't' => 3])) + $this->get($this->apiUrl('GetComments', ['i' => 'nonExistant'])) ->assertNotFound() ->assertJson([]); } @@ -170,7 +186,7 @@ public function testGetCommentsForUser(): void ]); // Act - $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) + $response = $this->get($this->apiUrl('GetComments', ['i' => $user->User, 't' => 3])) ->assertSuccessful(); // Assert @@ -212,7 +228,7 @@ public function testGetCommentsForUserWithDisabledWall(): void ]); // Act - $response = $this->get($this->apiUrl('GetComments', ['u' => $user->User, 't' => 3])) + $response = $this->get($this->apiUrl('GetComments', ['i' => $user->User])) ->assertNotFound() ->assertJson([]); } From 64c87733663526d0de16961ee9719b1dcdb06afd Mon Sep 17 00:00:00 2001 From: iOSLife Date: Thu, 5 Sep 2024 15:26:16 -0400 Subject: [PATCH 26/28] Remove check for if calling user is banned --- public/API/API_GetComments.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index 1c319e0527..1295177a3a 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -66,7 +66,7 @@ if ($username) { $user = User::firstWhere('User', $username); - if (!$user || !$user->UserWallActive || $user->banned_at || Auth::user()->banned_at) { + if (!$user || !$user->UserWallActive || $user->banned_at) { return response()->json([], 404); } } From 42c4c41e74cbfb0f5d0c7926f3c81c9e585eed3d Mon Sep 17 00:00:00 2001 From: iOSLife Date: Thu, 12 Sep 2024 13:17:59 -0400 Subject: [PATCH 27/28] Add wall and banned check to UserCommentPolicy --- app/Policies/UserCommentPolicy.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/app/Policies/UserCommentPolicy.php b/app/Policies/UserCommentPolicy.php index 742f117751..4f2f71d679 100644 --- a/app/Policies/UserCommentPolicy.php +++ b/app/Policies/UserCommentPolicy.php @@ -22,16 +22,14 @@ public function manage(User $user): bool public function viewAny(?User $user, User $commentable): bool { + if (!$commentable->UserWallActive || $commentable->banned_at) { + return false; + } + /* * check guests first */ if (!$user) { - /* - * TODO: check user privacy settings instead of wall_active flag - */ - // $commentable->preferences->wall - // return false; - return true; } @@ -42,12 +40,6 @@ public function viewAny(?User $user, User $commentable): bool return true; } - /* - * TODO: check user privacy settings instead of wall_active flag - */ - // $commentable->preferences->wall - // return false; - return true; } From ca72bca9aff325de670ba8321d74148a2cbaafca Mon Sep 17 00:00:00 2001 From: iOSLife Date: Thu, 12 Sep 2024 13:18:44 -0400 Subject: [PATCH 28/28] Use UserCommentPolicy viewAll in GetComments API --- public/API/API_GetComments.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/public/API/API_GetComments.php b/public/API/API_GetComments.php index 1295177a3a..4267a0eb32 100644 --- a/public/API/API_GetComments.php +++ b/public/API/API_GetComments.php @@ -4,6 +4,7 @@ use App\Models\Comment; use App\Models\User; use App\Policies\CommentPolicy; +use App\Policies\UserCommentPolicy; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; @@ -62,11 +63,12 @@ } $user = null; +$userPolicy = new UserCommentPolicy(); if ($username) { $user = User::firstWhere('User', $username); - if (!$user || !$user->UserWallActive || $user->banned_at) { + if (!$user || !$userPolicy->viewAny(null, $user)) { return response()->json([], 404); } } @@ -94,12 +96,12 @@ }) ->count(); -$policy = new CommentPolicy(); +$commentPolicy = new CommentPolicy(); -$results = $comments->filter(function ($nextComment) use ($policy) { +$results = $comments->filter(function ($nextComment) use ($commentPolicy) { $user = Auth::user() instanceof User ? Auth::user() : null; - return $policy->view($user, $nextComment); + return $commentPolicy->view($user, $nextComment); })->map(function ($nextComment) { return [ 'User' => $nextComment->user->username,