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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions app/Actions/UpdateUserProfile.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@
use App\Dto\GitHubProfile;
use App\Jobs\LookUpUserOrganizations;
use App\Models\User;
use App\Queries\GitHubReadmeQuery;
use App\Services\GitHub;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\DB;

/**
* Mirrors the GitHub profile and SSH keys of a user again, without them having to sign in, and looks the organizations
* their bio and company mention up.
* Mirrors the GitHub profile, profile README and SSH keys of a user again, without them having to sign in, and looks
* the organizations their bio and company mention up.
*/
final readonly class UpdateUserProfile
{
public function __construct(
private GitHub $github,
private UpdateUserSshKeys $updateUserSshKeys,
private UpdateStaleHandles $updateStaleHandles,
private GitHubReadmeQuery $readme,
) {
//
}
Expand Down Expand Up @@ -51,6 +53,8 @@ public function handle(User $user): bool
});

LookUpUserOrganizations::dispatchFor($user);

$this->readme->forget($user);
} catch (ConnectionException|RequestException|UniqueConstraintViolationException $exception) {
report($exception);

Expand Down
23 changes: 21 additions & 2 deletions app/Queries/GitHubReadmeQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public function __construct(private GitHub $github)
*/
public function get(User $user): string|false|null
{
$key = "github-readme:{$user->github_id}";
$failedKey = "github-readme-failed:{$user->github_id}";
$key = $this->key($user);
$failedKey = $this->failedKey($user);

/** @var string|null $html */
$html = Cache::get($key);
Expand All @@ -65,6 +65,25 @@ public function get(User $user): string|false|null
return $html === '' ? null : $html;
}

/**
* Read the README from GitHub again on the next view, as someone who syncs their profile expects to see theirs.
*/
public function forget(User $user): void
{
Cache::forget($this->key($user));
Cache::forget($this->failedKey($user));
}

private function key(User $user): string
{
return "github-readme:{$user->github_id}";
}

private function failedKey(User $user): string
{
return "github-readme-failed:{$user->github_id}";
}

/**
* @throws HttpClientException
*/
Expand Down
29 changes: 29 additions & 0 deletions tests/Unit/Actions/UpdateUserProfileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Jobs\LookUpUserOrganizations;
use App\Models\SshKey;
use App\Models\User;
use App\Queries\GitHubReadmeQuery;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
Expand Down Expand Up @@ -99,6 +100,34 @@
->and($user->sshKeys()->sole()->fingerprint)->toBe($publicKey->fingerprint());
});

it('reads the readme from github again, so an edited one shows after syncing', function (): void {
$user = User::factory()->create();

fakeGitHubAccount($user);
Http::fake(['api.github.com/repos/octocat/octocat/readme' => Http::sequence()->push('<p>Old</p>')->push('<p>New</p>')]);

expect(resolve(GitHubReadmeQuery::class)->get($user))->toBe('<p>Old</p>');

resolve(UpdateUserProfile::class)->handle($user);

expect(resolve(GitHubReadmeQuery::class)->get($user))->toBe('<p>New</p>');
});

it('keeps the cached readme when the sync fails', function (): void {
$user = User::factory()->create();

Http::fake([
"api.github.com/user/{$user->github_id}" => Http::sequence()->push(['login' => 'octocat'])->push(['message' => 'Error'], 500),
'api.github.com/repos/octocat/octocat/readme' => Http::sequence()->push('<p>Old</p>')->push('<p>New</p>'),
]);

expect(resolve(GitHubReadmeQuery::class)->get($user))->toBe('<p>Old</p>');

resolve(UpdateUserProfile::class)->handle($user);

expect(resolve(GitHubReadmeQuery::class)->get($user))->toBe('<p>Old</p>');
});

it('looks the account up by its github id, so a renamed handle cannot be claimed', function (): void {
$user = User::factory()->create(['github_id' => '583231', 'handle' => 'old-handle']);

Expand Down
16 changes: 16 additions & 0 deletions tests/Unit/Queries/GitHubReadmeQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ function fakeGitHubReadme(User $user, mixed $readme): void
expect(resolve(GitHubReadmeQuery::class)->get($user))->toBe('<p>Hi</p>');
});

it('reads the readme again once forgotten, a failed read included', function (): void {
$user = User::factory()->create();

Http::fake([
"api.github.com/user/{$user->github_id}" => Http::response(['login' => 'octocat']),
'api.github.com/repos/octocat/octocat/readme' => Http::sequence()->push('<p>Old</p>')->pushStatus(502)->push('<p>New</p>'),
]);
$readme = resolve(GitHubReadmeQuery::class);

expect($readme->get($user))->toBe('<p>Old</p>');
$readme->forget($user);
expect($readme->get($user))->toBeFalse();
$readme->forget($user);
expect($readme->get($user))->toBe('<p>New</p>');
});

it('keeps a readme with uploaded images for a few minutes, as their links expire', function (string $html, int $minutes, bool $isFetchedAgain): void {
$user = User::factory()->create();

Expand Down
Loading