Skip to content

Commit 7b24ffe

Browse files
Unique GitHub Usernames (#981)
* feat: add remove duplicate command * feat: resolve duplicate github usernames * feat: add job for resolving duplicate github usernames * username is required in job * username is required in job * remove github username job * Resolve duplicate github usernames * wip * wip --------- Co-authored-by: Dries Vints <[email protected]>
1 parent 336178e commit 7b24ffe

File tree

5 files changed

+135
-3
lines changed

5 files changed

+135
-3
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use App\Models\User;
7+
use Illuminate\Database\Eloquent\Collection;
8+
use App\Social\GithubUserApi;
9+
use Exception;
10+
11+
class ResolveDuplicateGithubUsernames extends Command
12+
{
13+
protected $signature = 'lio:resolve-duplicate-github-usernames';
14+
15+
protected $description = 'Resolve duplicate GitHub usernames from the database';
16+
17+
public function __construct(private GithubUserApi $github)
18+
{
19+
parent::__construct();
20+
}
21+
22+
public function handle(): void
23+
{
24+
$this->info('Resolving duplicate Github usernames...');
25+
26+
$this->duplicates()
27+
->groupBy('github_username')
28+
->each(function (Collection $users) {
29+
$uniqueUsernames = [];
30+
31+
// Order from the latest user to the oldest
32+
$users = $users->sortByDesc('created_at');
33+
34+
// Resolve each user with the same github_username
35+
foreach ($users as $user) {
36+
try {
37+
$githubUser = $this->github->find($user->github_id);
38+
} catch (Exception $e) {
39+
$message = $e->getCode() === 404 ? "Error 404: github_id - $user->github_id" : $e->getMessage();
40+
$this->error($message);
41+
continue;
42+
}
43+
44+
// If the user doesn't exist on GitHub
45+
if (! $githubUser || ! $githubUser->login()) {
46+
$user->update(['github_username' => null]);
47+
continue;
48+
}
49+
50+
// If the user's github_username marked as unique
51+
if (in_array($githubUser->login(), $uniqueUsernames)) {
52+
$user->update(['github_username' => null]);
53+
continue;
54+
}
55+
56+
// If the user exists on GitHub, update the user's github_username
57+
$user->update(['github_username' => $githubUser->login()]);
58+
59+
$this->info("Updated user: {$user->id} with github_username: {$githubUser->login()}");
60+
61+
// Mark the user's github_username as unique
62+
$uniqueUsernames[] = $githubUser->login();
63+
}
64+
});
65+
66+
$this->info('Duplicate Github usernames resolved!');
67+
}
68+
69+
private function duplicates(): Collection
70+
{
71+
return User::whereIn('github_username', function ($query) {
72+
$query->select('github_username')
73+
->from('users')
74+
->whereNotNull('github_username')
75+
->where('github_username', '!=', '')
76+
->groupBy('github_username')
77+
->havingRaw('COUNT(github_username) > 1');
78+
})->get();
79+
}
80+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Listeners;
6+
7+
use App\Models\User;
8+
use App\Social\GithubUserApi;
9+
use Illuminate\Auth\Events\Registered;
10+
11+
final class ResolveDuplicateGitHubUsername
12+
{
13+
public function __construct(private readonly GithubUserApi $github)
14+
{
15+
}
16+
17+
public function handle(Registered $event): void
18+
{
19+
User::where('github_username', $event->user->github_username)
20+
->whereKeyNot($event->user)
21+
->latest()
22+
->get()
23+
->each(function (User $user) {
24+
$user->update([
25+
'github_username' => $this->github->find($user->github_id)?->login(),
26+
]);
27+
});
28+
}
29+
}

app/Providers/EventServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use App\Listeners\NotifyUsersMentionedInReply;
1313
use App\Listeners\NotifyUsersMentionedInThread;
1414
use App\Listeners\RenewEmailVerificationNotification;
15+
use App\Listeners\ResolveDuplicateGitHubUsername;
1516
use App\Listeners\SendArticleApprovedNotification;
1617
use App\Listeners\SendNewArticleNotification;
1718
use App\Listeners\SendNewReplyNotification;
@@ -42,6 +43,7 @@ class EventServiceProvider extends ServiceProvider
4243
RenewEmailVerificationNotification::class,
4344
],
4445
Registered::class => [
46+
ResolveDuplicateGitHubUsername::class,
4547
SendEmailVerificationNotification::class,
4648
],
4749
ReplyWasCreated::class => [

app/Social/GitHubUser.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88

99
final class GitHubUser implements Arrayable
1010
{
11-
public function __construct(
12-
private array $attributes
13-
) {
11+
public function __construct(private array $attributes)
12+
{
13+
}
14+
15+
public function login(): string
16+
{
17+
return $this->get('login');
1418
}
1519

1620
public function hasPublicRepositories(): bool

app/Social/GithubUserApi.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace App\Social;
4+
5+
use Illuminate\Support\Facades\Http;
6+
use Illuminate\Http\Client\ConnectionException;
7+
8+
class GithubUserApi
9+
{
10+
public function find(int|string $id): ?GitHubUser
11+
{
12+
$response = Http::retry(3, 100, fn ($exception) => $exception instanceof ConnectionException)
13+
->get("https://api.github.com/user/{$id}");
14+
15+
return $response->failed() ? null : new GitHubUser($response->json());
16+
}
17+
}

0 commit comments

Comments
 (0)