Migrate outbound HTTP calls from Guzzle to the Http facade #104
26 changed files with 337 additions and 630 deletions
|
|
@ -6,11 +6,11 @@ namespace App\Http\Controllers\Admin;
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Contact;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\BadResponseException;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ContactsController extends Controller
|
||||
|
|
@ -113,14 +113,13 @@ class ContactsController extends Controller
|
|||
$avatar = null;
|
||||
$contact = Contact::findOrFail($contactId);
|
||||
if ($contact->homepage !== null && mb_strlen($contact->homepage) !== 0) {
|
||||
$client = resolve(Client::class);
|
||||
try {
|
||||
$response = $client->get($contact->homepage);
|
||||
} catch (BadResponseException $e) {
|
||||
$response = Http::throw()->get($contact->homepage);
|
||||
} catch (RequestException $e) {
|
||||
return redirect('/admin/contacts/'.$contactId.'/edit')
|
||||
->with('error', 'Bad resposne from contact’s homepage');
|
||||
}
|
||||
$mf2 = \Mf2\parse((string) $response->getBody(), $contact->homepage);
|
||||
$mf2 = \Mf2\parse($response->body(), $contact->homepage);
|
||||
foreach ($mf2['items'] as $microformat) {
|
||||
if (Arr::get($microformat, 'type.0') === 'h-card') {
|
||||
$avatarURL = Arr::get($microformat, 'properties.photo.0.value');
|
||||
|
|
@ -129,8 +128,8 @@ class ContactsController extends Controller
|
|||
}
|
||||
if ($avatarURL !== null) {
|
||||
try {
|
||||
$avatar = $client->get($avatarURL);
|
||||
} catch (BadResponseException $e) {
|
||||
$avatar = Http::throw()->get($avatarURL);
|
||||
} catch (RequestException $e) {
|
||||
return redirect('/admin/contacts/'.$contactId.'/edit')
|
||||
->with('error', 'Unable to download avatar');
|
||||
}
|
||||
|
|
@ -141,7 +140,7 @@ class ContactsController extends Controller
|
|||
if ($filesystem->isDirectory($directory) === false) {
|
||||
$filesystem->makeDirectory($directory);
|
||||
}
|
||||
$filesystem->put($directory.'/image', $avatar->getBody());
|
||||
$filesystem->put($directory.'/image', $avatar->body());
|
||||
|
||||
return view('admin.contacts.getavatarsuccess', [
|
||||
'homepage' => parse_url($contact->homepage, PHP_URL_HOST),
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ declare(strict_types=1);
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\TokenService;
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\View\View;
|
||||
use Random\RandomException;
|
||||
|
|
@ -199,15 +198,13 @@ class IndieAuthController extends Controller
|
|||
}
|
||||
|
||||
// Otherwise we need to check the redirect_uri is in the client_id's redirect_uris
|
||||
$guzzle = resolve(Client::class);
|
||||
|
||||
try {
|
||||
$clientInfo = $guzzle->get($clientId);
|
||||
} catch (Exception) {
|
||||
$clientInfo = Http::throw()->get($clientId);
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$clientInfoParsed = \Mf2\parse($clientInfo->getBody()->getContents(), $clientId);
|
||||
$clientInfoParsed = \Mf2\parse($clientInfo->body(), $clientId);
|
||||
|
||||
$redirectUris = $clientInfoParsed['rels']['redirect_uri'] ?? [];
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\FileSystem\FileSystem;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class DownloadWebMention implements ShouldQueue
|
||||
{
|
||||
|
|
@ -29,15 +29,15 @@ class DownloadWebMention implements ShouldQueue
|
|||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws RequestException
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public function handle(Client $guzzle): void
|
||||
public function handle(): void
|
||||
{
|
||||
$response = $guzzle->request('GET', $this->source);
|
||||
// 4XX and 5XX responses should get Guzzle to throw an exception,
|
||||
// Laravel should catch and retry these automatically.
|
||||
if ($response->getStatusCode() === 200) {
|
||||
// 4XX and 5XX responses should throw so Laravel can catch and
|
||||
// retry these automatically.
|
||||
$response = Http::throw()->get($this->source);
|
||||
if ($response->status() === 200) {
|
||||
$filesystem = new FileSystem;
|
||||
$filename = storage_path('HTML').'/'.$this->createFilenameFromURL($this->source);
|
||||
// backup file first
|
||||
|
|
@ -56,7 +56,7 @@ class DownloadWebMention implements ShouldQueue
|
|||
// save new HTML
|
||||
$filesystem->put(
|
||||
$filename,
|
||||
(string) $response->getBody()
|
||||
$response->body()
|
||||
);
|
||||
// remove backup if the same
|
||||
if ($filesystem->exists($filenameBackup)) {
|
||||
|
|
|
|||
|
|
@ -5,14 +5,13 @@ declare(strict_types=1);
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Like;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||
|
||||
|
|
@ -32,13 +31,11 @@ class ProcessLike implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(Client $client, Authorship $authorship): int
|
||||
public function handle(Authorship $authorship): int
|
||||
{
|
||||
$response = $client->request('GET', $this->like->url);
|
||||
$mf2 = \Mf2\parse((string) $response->getBody(), $this->like->url);
|
||||
$response = Http::throw()->get($this->like->url);
|
||||
$mf2 = \Mf2\parse($response->body(), $this->like->url);
|
||||
if (Arr::has($mf2, 'items.0.properties.content')) {
|
||||
$this->like->content = $mf2['items'][0]['properties']['content'][0]['html'];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,12 @@ namespace App\Jobs;
|
|||
use App\Exceptions\RemoteContentNotFoundException;
|
||||
use App\Models\Note;
|
||||
use App\Models\WebMention;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\WebmentionsParser\Exceptions\InvalidMentionException;
|
||||
use Jonnybarnes\WebmentionsParser\Parser;
|
||||
use Mf2;
|
||||
|
|
@ -36,18 +35,20 @@ class ProcessWebMention implements ShouldQueue
|
|||
* Execute the job.
|
||||
*
|
||||
* @throws RemoteContentNotFoundException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidMentionException
|
||||
*/
|
||||
public function handle(Parser $parser, Client $guzzle): void
|
||||
public function handle(Parser $parser): void
|
||||
{
|
||||
try {
|
||||
$response = $guzzle->request('GET', $this->source);
|
||||
} catch (RequestException $e) {
|
||||
$response = Http::get($this->source);
|
||||
} catch (ConnectionException) {
|
||||
throw new RemoteContentNotFoundException;
|
||||
}
|
||||
$this->saveRemoteContent((string) $response->getBody(), $this->source);
|
||||
$microformats = Mf2\parse((string) $response->getBody(), $this->source);
|
||||
if ($response->failed()) {
|
||||
throw new RemoteContentNotFoundException;
|
||||
}
|
||||
$this->saveRemoteContent($response->body(), $this->source);
|
||||
$microformats = Mf2\parse($response->body(), $this->source);
|
||||
$webmentions = WebMention::where('source', $this->source)->get();
|
||||
foreach ($webmentions as $webmention) {
|
||||
// check webmention still references target
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||
|
||||
|
|
@ -55,12 +56,10 @@ class SaveProfileImage implements ShouldQueue
|
|||
&& parse_url($photo, PHP_URL_HOST) !== 'pbs.twimg.com'
|
||||
&& parse_url($photo, PHP_URL_HOST) !== 'twitter.com'
|
||||
) {
|
||||
$client = resolve(Client::class);
|
||||
|
||||
try {
|
||||
$response = $client->get($photo);
|
||||
$image = $response->getBody();
|
||||
} catch (RequestException) {
|
||||
$response = Http::throw()->get($photo);
|
||||
$image = $response->body();
|
||||
} catch (ConnectionException|RequestException) {
|
||||
// we are opening and reading the default image so that
|
||||
$default = public_path().'/assets/profile-images/default-image';
|
||||
$handle = fopen($default, 'rb');
|
||||
|
|
|
|||
|
|
@ -5,14 +5,15 @@ declare(strict_types=1);
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Bookmark;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Http\Client\PendingRequest;
|
||||
use Illuminate\Http\Client\Response;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use JsonException;
|
||||
|
||||
class SaveScreenshot implements ShouldQueue
|
||||
{
|
||||
|
|
@ -27,77 +28,68 @@ class SaveScreenshot implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
*
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
// A normal Guzzle client
|
||||
$client = resolve(Client::class);
|
||||
// A Guzzle client with a custom Middleware to retry the CloudConvert API requests
|
||||
$retryClient = resolve('RetryGuzzle');
|
||||
$cloudConvert = Http::baseUrl('https://api.cloudconvert.com/v2')
|
||||
->withToken(config('services.cloudconvert.token'))
|
||||
->throw();
|
||||
|
||||
// First request that CloudConvert takes a screenshot of the URL
|
||||
$takeScreenshotJobResponse = $client->request('POST', 'https://api.cloudconvert.com/v2/capture-website', [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
||||
],
|
||||
'json' => [
|
||||
'url' => $this->bookmark->url,
|
||||
'output_format' => 'png',
|
||||
'screen_width' => 1440,
|
||||
'screen_height' => 900,
|
||||
'wait_until' => 'networkidle0',
|
||||
'wait_time' => 100,
|
||||
],
|
||||
$takeScreenshotJobResponse = $cloudConvert->post('/capture-website', [
|
||||
'url' => $this->bookmark->url,
|
||||
'output_format' => 'png',
|
||||
'screen_width' => 1440,
|
||||
'screen_height' => 900,
|
||||
'wait_until' => 'networkidle0',
|
||||
'wait_time' => 100,
|
||||
]);
|
||||
|
||||
$taskId = json_decode($takeScreenshotJobResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
|
||||
$taskId = $takeScreenshotJobResponse->json('data.id');
|
||||
|
||||
// Now wait till the status job is finished
|
||||
$screenshotJobStatusResponse = $retryClient->request('GET', 'https://api.cloudconvert.com/v2/tasks/'.$taskId, [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
||||
],
|
||||
'query' => [
|
||||
'include' => 'payload',
|
||||
],
|
||||
]);
|
||||
$screenshotJobStatusResponse = $this->pollUntilFinished($cloudConvert, $taskId);
|
||||
|
||||
$finishedCaptureId = json_decode($screenshotJobStatusResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
|
||||
$finishedCaptureId = $screenshotJobStatusResponse->json('data.id');
|
||||
|
||||
// Now we can create a new job to request thst the screenshot is exported to a temporary URL we can download the screenshot from
|
||||
$exportImageJob = $client->request('POST', 'https://api.cloudconvert.com/v2/export/url', [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
||||
],
|
||||
'json' => [
|
||||
'input' => $finishedCaptureId,
|
||||
'archive_multiple_files' => false,
|
||||
],
|
||||
$exportImageJob = $cloudConvert->post('/export/url', [
|
||||
'input' => $finishedCaptureId,
|
||||
'archive_multiple_files' => false,
|
||||
]);
|
||||
|
||||
$exportImageJobId = json_decode($exportImageJob->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
|
||||
$exportImageJobId = $exportImageJob->json('data.id');
|
||||
|
||||
// Again, wait till the status of this export job is finished
|
||||
$finalImageUrlResponse = $retryClient->request('GET', 'https://api.cloudconvert.com/v2/tasks/'.$exportImageJobId, [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
||||
],
|
||||
'query' => [
|
||||
'include' => 'payload',
|
||||
],
|
||||
]);
|
||||
$finalImageUrlResponse = $this->pollUntilFinished($cloudConvert, $exportImageJobId);
|
||||
|
||||
// Now we can download the screenshot and save it to the storage
|
||||
$finalImageUrl = json_decode($finalImageUrlResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->result->files[0]->url;
|
||||
$finalImageUrl = $finalImageUrlResponse->json('data.result.files.0.url');
|
||||
|
||||
$finalImageUrlContent = $client->request('GET', $finalImageUrl);
|
||||
$finalImageUrlContent = Http::throw()->get($finalImageUrl);
|
||||
|
||||
Storage::disk('public')->put('/assets/img/bookmarks/'.$taskId.'.png', $finalImageUrlContent->getBody()->getContents());
|
||||
Storage::disk('public')->put('/assets/img/bookmarks/'.$taskId.'.png', $finalImageUrlContent->body());
|
||||
|
||||
$this->bookmark->screenshot = $taskId;
|
||||
$this->bookmark->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll a CloudConvert task until it reports a "finished" status.
|
||||
*/
|
||||
private function pollUntilFinished(PendingRequest $client, string $taskId): Response
|
||||
{
|
||||
$attempts = 0;
|
||||
|
||||
do {
|
||||
$response = $client->get('/tasks/'.$taskId, ['include' => 'payload']);
|
||||
$finished = $response->json('data.status') === 'finished';
|
||||
if (! $finished) {
|
||||
$attempts++;
|
||||
usleep(1_000_000); // 1 second, matches CloudConvert's own polling guidance
|
||||
}
|
||||
} while (! $finished && $attempts < 5);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ declare(strict_types=1);
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Note;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Psr7\Header;
|
||||
use GuzzleHttp\Psr7\UriResolver;
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
|
|
@ -14,6 +12,7 @@ use Illuminate\Bus\Queueable;
|
|||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
use Mf2\Parser;
|
||||
|
||||
|
|
@ -32,8 +31,6 @@ class SendWebMentions implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
|
|
@ -43,12 +40,9 @@ class SendWebMentions implements ShouldQueue
|
|||
foreach ($urls as $url) {
|
||||
$endpoint = $this->discoverWebmentionEndpoint($url);
|
||||
if ($endpoint !== null) {
|
||||
$guzzle = resolve(Client::class);
|
||||
$guzzle->post($endpoint, [
|
||||
'form_params' => [
|
||||
'source' => $this->note->uri,
|
||||
'target' => $url,
|
||||
],
|
||||
Http::asForm()->post($endpoint, [
|
||||
'source' => $this->note->uri,
|
||||
'target' => $url,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -56,8 +50,6 @@ class SendWebMentions implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Discover if a URL has a webmention endpoint.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function discoverWebmentionEndpoint(string $url): ?string
|
||||
{
|
||||
|
|
@ -71,10 +63,9 @@ class SendWebMentions implements ShouldQueue
|
|||
|
||||
$endpoint = null;
|
||||
|
||||
$guzzle = resolve(Client::class);
|
||||
$response = $guzzle->get($url);
|
||||
$response = Http::get($url);
|
||||
// check HTTP Headers for webmention endpoint
|
||||
$links = Header::parse($response->getHeader('Link'));
|
||||
$links = Header::parse($response->header('Link'));
|
||||
foreach ($links as $link) {
|
||||
if (array_key_exists('rel', $link) && mb_stristr($link['rel'], 'webmention')) {
|
||||
return $this->resolveUri(trim($link[0], '<>'), $url);
|
||||
|
|
@ -82,7 +73,7 @@ class SendWebMentions implements ShouldQueue
|
|||
}
|
||||
|
||||
// failed to find a header so parse HTML
|
||||
$html = (string) $response->getBody();
|
||||
$html = $response->body();
|
||||
|
||||
if ($html === '') {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ declare(strict_types=1);
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Note;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class SyndicateNoteToBluesky implements ShouldQueue
|
||||
{
|
||||
|
|
@ -31,29 +30,18 @@ class SyndicateNoteToBluesky implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(Client $guzzle): void
|
||||
public function handle(): void
|
||||
{
|
||||
$response = $guzzle->request(
|
||||
'POST',
|
||||
'https://brid.gy/publish/webmention',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'form_params' => [
|
||||
'source' => $this->note->uri,
|
||||
'target' => 'https://brid.gy/publish/bluesky',
|
||||
],
|
||||
'http_errors' => false,
|
||||
]
|
||||
);
|
||||
// no ->throw()/->retry() here — Bridgy would duplicate-publish on retry, see $tries above
|
||||
$response = Http::acceptJson()->asForm()->post('https://brid.gy/publish/webmention', [
|
||||
'source' => $this->note->uri,
|
||||
'target' => 'https://brid.gy/publish/bluesky',
|
||||
]);
|
||||
|
||||
$body = json_decode((string) $response->getBody(), true);
|
||||
$body = $response->json();
|
||||
|
||||
if ($response->getStatusCode() === 201) {
|
||||
if ($response->status() === 201) {
|
||||
$this->note->bluesky_url = $body['url'];
|
||||
$this->note->save();
|
||||
|
||||
|
|
@ -61,7 +49,7 @@ class SyndicateNoteToBluesky implements ShouldQueue
|
|||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
'Bridgy publish to Bluesky failed: '.($body['error'] ?? (string) $response->getBody())
|
||||
'Bridgy publish to Bluesky failed: '.($body['error'] ?? $response->body())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ declare(strict_types=1);
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Note;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class SyndicateNoteToMastodon implements ShouldQueue
|
||||
{
|
||||
|
|
@ -31,29 +30,18 @@ class SyndicateNoteToMastodon implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(Client $guzzle): void
|
||||
public function handle(): void
|
||||
{
|
||||
$response = $guzzle->request(
|
||||
'POST',
|
||||
'https://brid.gy/publish/webmention',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'form_params' => [
|
||||
'source' => $this->note->uri,
|
||||
'target' => 'https://brid.gy/publish/mastodon',
|
||||
],
|
||||
'http_errors' => false,
|
||||
]
|
||||
);
|
||||
// no ->throw()/->retry() here — Bridgy would duplicate-publish on retry, see $tries above
|
||||
$response = Http::acceptJson()->asForm()->post('https://brid.gy/publish/webmention', [
|
||||
'source' => $this->note->uri,
|
||||
'target' => 'https://brid.gy/publish/mastodon',
|
||||
]);
|
||||
|
||||
$body = json_decode((string) $response->getBody(), true);
|
||||
$body = $response->json();
|
||||
|
||||
if ($response->getStatusCode() === 201) {
|
||||
if ($response->status() === 201) {
|
||||
$this->note->mastodon_url = $body['url'];
|
||||
$this->note->save();
|
||||
|
||||
|
|
@ -61,7 +49,7 @@ class SyndicateNoteToMastodon implements ShouldQueue
|
|||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
'Bridgy publish to Mastodon failed: '.($body['error'] ?? (string) $response->getBody())
|
||||
'Bridgy publish to Mastodon failed: '.($body['error'] ?? $response->body())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ namespace App\Models;
|
|||
use App\CommonMark\Generators\MentionGenerator;
|
||||
use App\CommonMark\Renderers\MentionRenderer;
|
||||
use App\Observers\NoteObserver;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||
|
|
@ -20,6 +19,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\IndieWeb\Numbers;
|
||||
use Laravel\Scout\Searchable;
|
||||
use League\CommonMark\Environment\Environment;
|
||||
|
|
@ -363,18 +363,15 @@ class Note extends Model
|
|||
$latLng = $latitude.','.$longitude;
|
||||
|
||||
return Cache::get($latLng, function () use ($latLng, $latitude, $longitude) {
|
||||
$guzzle = resolve(Client::class);
|
||||
$response = $guzzle->request('GET', 'https://nominatim.openstreetmap.org/reverse', [
|
||||
'query' => [
|
||||
$response = Http::withHeaders(['User-Agent' => 'jonnybarnes.uk, email jonny@jonnybarnes.uk'])
|
||||
->get('https://nominatim.openstreetmap.org/reverse', [
|
||||
'format' => 'json',
|
||||
'lat' => $latitude,
|
||||
'lon' => $longitude,
|
||||
'zoom' => 18,
|
||||
'addressdetails' => 1,
|
||||
],
|
||||
'headers' => ['User-Agent' => 'jonnybarnes.uk via Guzzle, email jonny@jonnybarnes.uk'],
|
||||
]);
|
||||
$json = json_decode((string) $response->getBody());
|
||||
]);
|
||||
$json = $response->object();
|
||||
if (isset($json->address->suburb)) {
|
||||
$locality = $json->address->suburb;
|
||||
if (isset($json->address->city)) {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
|
@ -82,38 +78,6 @@ class AppServiceProvider extends ServiceProvider
|
|||
);
|
||||
});
|
||||
|
||||
// Configure Guzzle
|
||||
$this->app->bind('RetryGuzzle', function () {
|
||||
$handlerStack = HandlerStack::create();
|
||||
$handlerStack->push(Middleware::retry(
|
||||
function ($retries, $request, $response, $exception) {
|
||||
// Limit the number of retries to 5
|
||||
if ($retries >= 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retry connection exceptions
|
||||
if ($exception instanceof ConnectException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retry on server errors
|
||||
if ($response && $response->getStatusCode() >= 500) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finally for CloudConvert, retry if status is not final
|
||||
return json_decode($response, false, 512, JSON_THROW_ON_ERROR)->data->status !== 'finished';
|
||||
},
|
||||
function () {
|
||||
// Retry after 1 second
|
||||
return 1000;
|
||||
}
|
||||
));
|
||||
|
||||
return new Client(['handler' => $handlerStack]);
|
||||
});
|
||||
|
||||
// Turn on Eloquent strict mode when developing
|
||||
Model::shouldBeStrict(! $this->app->isProduction());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@ use App\Exceptions\InternetArchiveException;
|
|||
use App\Jobs\ProcessBookmark;
|
||||
use App\Models\Bookmark;
|
||||
use App\Models\Tag;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BookmarkService
|
||||
|
|
@ -55,21 +53,19 @@ class BookmarkService
|
|||
* Given a URL, attempt to save it to the Internet Archive.
|
||||
*
|
||||
* @throws InternetArchiveException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function getArchiveLink(string $url): string
|
||||
{
|
||||
$client = resolve(Client::class);
|
||||
try {
|
||||
$response = $client->request('GET', 'https://web.archive.org/save/'.$url);
|
||||
} catch (ClientException $e) {
|
||||
$response = Http::get('https://web.archive.org/save/'.$url);
|
||||
|
||||
if ($response->clientError()) {
|
||||
// throw an exception to be caught
|
||||
throw new InternetArchiveException;
|
||||
}
|
||||
if ($response->hasHeader('Content-Location')) {
|
||||
if (Str::startsWith(Arr::get($response->getHeader('Content-Location'), 0), '/web')) {
|
||||
return $response->getHeader('Content-Location')[0];
|
||||
}
|
||||
|
||||
$contentLocation = $response->header('Content-Location');
|
||||
if ($contentLocation !== '' && Str::startsWith($contentLocation, '/web')) {
|
||||
return $contentLocation;
|
||||
}
|
||||
|
||||
// throw an exception to be caught
|
||||
|
|
|
|||
|
|
@ -6,12 +6,9 @@ namespace Tests\Feature\Admin;
|
|||
|
||||
use App\Models\Contact;
|
||||
use App\Models\User;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -141,14 +138,12 @@ class ContactsTest extends TestCase
|
|||
<img class="u-photo" alt="" src="http://tantek.com/tantek.png">
|
||||
</div>
|
||||
HTML;
|
||||
$file = fopen(__DIR__.'/../../aaron.png', 'rb');
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'text/html'], $html),
|
||||
new Response(200, ['Content-Type' => 'image/png'], $file),
|
||||
$file = file_get_contents(__DIR__.'/../../aaron.png');
|
||||
Http::fake([
|
||||
'*' => Http::sequence()
|
||||
->push($html, 200, ['Content-Type' => 'text/html'])
|
||||
->push($file, 200, ['Content-Type' => 'image/png']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$user = User::factory()->make();
|
||||
$contact = Contact::factory()->create([
|
||||
'homepage' => 'https://tantek.com',
|
||||
|
|
@ -165,12 +160,9 @@ class ContactsTest extends TestCase
|
|||
#[Test]
|
||||
public function getting_remote_avatar_fails_gracefully_with_remote_not_found(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(404),
|
||||
Http::fake([
|
||||
'*' => Http::response('', 404),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$user = User::factory()->make();
|
||||
$contact = Contact::factory()->create();
|
||||
|
||||
|
|
@ -187,13 +179,11 @@ class ContactsTest extends TestCase
|
|||
<img class="u-photo" src="http://tantek.com/tantek.png">
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'text/html'], $html),
|
||||
new Response(404),
|
||||
Http::fake([
|
||||
'*' => Http::sequence()
|
||||
->push($html, 200, ['Content-Type' => 'text/html'])
|
||||
->push('', 404),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$user = User::factory()->make();
|
||||
$contact = Contact::factory()->create();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@ declare(strict_types=1);
|
|||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\User;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use GuzzleHttp\Psr7\UriResolver;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -269,12 +266,9 @@ class IndieAuthTest extends TestCase
|
|||
</html>
|
||||
HTML;
|
||||
|
||||
$mockHandler = new MockHandler([
|
||||
new Response(200, [], $appPageHtml),
|
||||
Http::fake([
|
||||
'*' => Http::response($appPageHtml, 200),
|
||||
]);
|
||||
$handlerStack = HandlerStack::create($mockHandler);
|
||||
$mockGuzzleClient = new Client(['handler' => $handlerStack]);
|
||||
$this->app->instance(Client::class, $mockGuzzleClient);
|
||||
|
||||
$user = User::factory()->make();
|
||||
$url = url()->query('/auth', [
|
||||
|
|
@ -313,12 +307,9 @@ class IndieAuthTest extends TestCase
|
|||
</html>
|
||||
HTML;
|
||||
|
||||
$mockHandler = new MockHandler([
|
||||
new Response(200, [], $appPageHtml),
|
||||
Http::fake([
|
||||
'*' => Http::response($appPageHtml, 200),
|
||||
]);
|
||||
$handlerStack = HandlerStack::create($mockHandler);
|
||||
$mockGuzzleClient = new Client(['handler' => $handlerStack]);
|
||||
$this->app->instance(Client::class, $mockGuzzleClient);
|
||||
|
||||
$user = User::factory()->make();
|
||||
$url = url()->query('/auth', [
|
||||
|
|
|
|||
|
|
@ -6,11 +6,8 @@ namespace Tests\Feature;
|
|||
|
||||
use App\Jobs\ProcessLike;
|
||||
use App\Models\Like;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
|
@ -98,18 +95,12 @@ class LikesTest extends TestCase
|
|||
</html>
|
||||
END;
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $content),
|
||||
new Response(200, [], $content),
|
||||
Http::fake([
|
||||
'*' => Http::response($content, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->bind(Client::class, function () use ($client) {
|
||||
return $client;
|
||||
});
|
||||
$authorship = new Authorship;
|
||||
|
||||
$job->handle($client, $authorship);
|
||||
$job->handle($authorship);
|
||||
|
||||
$this->assertEquals('Fred Bloggs', Like::find($id)->author_name);
|
||||
}
|
||||
|
|
@ -141,18 +132,12 @@ class LikesTest extends TestCase
|
|||
</html>
|
||||
END;
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $content),
|
||||
new Response(200, [], $content),
|
||||
Http::fake([
|
||||
'*' => Http::response($content, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->bind(Client::class, function () use ($client) {
|
||||
return $client;
|
||||
});
|
||||
$authorship = new Authorship;
|
||||
|
||||
$job->handle($client, $authorship);
|
||||
$job->handle($authorship);
|
||||
|
||||
$this->assertEquals('Fred Bloggs', Like::find($id)->author_name);
|
||||
}
|
||||
|
|
@ -177,18 +162,12 @@ class LikesTest extends TestCase
|
|||
</html>
|
||||
END;
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $content),
|
||||
new Response(200, [], $content),
|
||||
Http::fake([
|
||||
'*' => Http::response($content, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->bind(Client::class, function () use ($client) {
|
||||
return $client;
|
||||
});
|
||||
$authorship = new Authorship;
|
||||
|
||||
$job->handle($client, $authorship);
|
||||
$job->handle($authorship);
|
||||
|
||||
$this->assertNull(Like::find($id)->author_name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,19 @@
|
|||
namespace Tests;
|
||||
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
use CreatesApplication;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Http::preventStrayRequests();
|
||||
}
|
||||
|
||||
public function removeDirIfEmpty(string $dir): void
|
||||
{
|
||||
// scandir() will always return `.` and `..` so even an “empty”
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ namespace Tests\Unit;
|
|||
|
||||
use App\Exceptions\InternetArchiveException;
|
||||
use App\Services\BookmarkService;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -29,12 +26,9 @@ class BookmarksTest extends TestCase
|
|||
#[Test]
|
||||
public function archive_link_method_calls_archive_service(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Location' => '/web/1234/example.org']),
|
||||
Http::fake([
|
||||
'web.archive.org/*' => Http::response('', 200, ['Content-Location' => '/web/1234/example.org']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$url = (new BookmarkService)->getArchiveLink('https://example.org');
|
||||
$this->assertEquals('/web/1234/example.org', $url);
|
||||
}
|
||||
|
|
@ -44,12 +38,9 @@ class BookmarksTest extends TestCase
|
|||
{
|
||||
$this->expectException(InternetArchiveException::class);
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(403),
|
||||
Http::fake([
|
||||
'web.archive.org/*' => Http::response('', 403),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
(new BookmarkService)->getArchiveLink('https://example.org');
|
||||
}
|
||||
|
||||
|
|
@ -58,12 +49,9 @@ class BookmarksTest extends TestCase
|
|||
{
|
||||
$this->expectException(InternetArchiveException::class);
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(200),
|
||||
Http::fake([
|
||||
'web.archive.org/*' => Http::response('', 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
(new BookmarkService)->getArchiveLink('https://example.org');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,8 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Jobs;
|
||||
|
||||
use App\Jobs\DownloadWebMention;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\FileSystem\FileSystem;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -35,19 +32,16 @@ class DownloadWebMentionJobTest extends TestCase
|
|||
</div>
|
||||
HTML;
|
||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
||||
Http::fake([
|
||||
'example.org/*' => Http::response($html, 200, ['X-Foo' => 'Bar']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$job = new DownloadWebMention($source);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertFileExists(storage_path('HTML/https'));
|
||||
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertFileDoesNotExist(storage_path('HTML/https/example.org/reply').'/1.'.date('Y-m-d').'.backup');
|
||||
}
|
||||
|
|
@ -70,19 +64,18 @@ class DownloadWebMentionJobTest extends TestCase
|
|||
HTML;
|
||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||
$html2 = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html2);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html2),
|
||||
Http::fake([
|
||||
'example.org/*' => Http::sequence()
|
||||
->push($html, 200, ['X-Foo' => 'Bar'])
|
||||
->push($html2, 200, ['X-Foo' => 'Bar']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$job = new DownloadWebMention($source);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertFileExists(storage_path('HTML/https'));
|
||||
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertFileExists(storage_path('HTML/https/example.org/reply').'/1.'.date('Y-m-d').'.backup');
|
||||
}
|
||||
|
|
@ -98,14 +91,12 @@ class DownloadWebMentionJobTest extends TestCase
|
|||
</div>
|
||||
HTML;
|
||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
||||
Http::fake([
|
||||
'example.org/*' => Http::response($html, 200, ['X-Foo' => 'Bar']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$job = new DownloadWebMention($source);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertFileExists(storage_path('HTML/https/example.org/reply-one/index.html'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,9 @@ use App\Jobs\ProcessWebMention;
|
|||
use App\Jobs\SaveProfileImage;
|
||||
use App\Models\Note;
|
||||
use App\Models\WebMention;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\FileSystem\FileSystem;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Jonnybarnes\WebmentionsParser\Parser;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
|
@ -39,17 +36,15 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
$this->expectException(RemoteContentNotFoundException::class);
|
||||
|
||||
$parser = new Parser;
|
||||
$mock = new MockHandler([
|
||||
new Response(404),
|
||||
Http::fake([
|
||||
'*' => Http::response('', 404),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/mention/1/';
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
|
@ -65,17 +60,15 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
</div>
|
||||
HTML;
|
||||
$html = str_replace('href="', 'href="'.config('app.url'), $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/mention/1/';
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
Queue::assertPushed(SaveProfileImage::class);
|
||||
$this->assertDatabaseHas('webmentions', [
|
||||
|
|
@ -103,14 +96,12 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
<div class="e-content">Updated reply</div>
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
Queue::assertPushed(SaveProfileImage::class);
|
||||
$this->assertDatabaseHas('webmentions', [
|
||||
|
|
@ -132,11 +123,9 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
<div class="e-content">Replying to someone else</div>
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/reply/1';
|
||||
|
|
@ -151,7 +140,7 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
]);
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
$this->assertDatabaseMissing('webmentions', [
|
||||
'source' => $source,
|
||||
|
|
@ -169,11 +158,9 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
<div class="e-content">I like someone else now</div>
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/reply/1';
|
||||
|
|
@ -188,7 +175,7 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
]);
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
$this->assertDatabaseMissing('webmentions', [
|
||||
'source' => $source,
|
||||
|
|
@ -208,18 +195,16 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
</div>
|
||||
HTML;
|
||||
$html = str_replace('href="', 'href="'.config('app.url'), $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
// Simulate a long brid.gy Bluesky source URL (well over 255 characters)
|
||||
$source = 'https://brid.gy/comment/bluesky/did:plc:n3jhgiq2ykctnpgzlm6p6b25/at%253A%252F%252Fdid%253Aplc%253An3jhgiq2ykctnpgzlm6p6b25%252Fapp.bsky.feed.post%252F3mfekjuhykb2k/at%253A%252F%252Fdid%253Aplc%253Afsfuwigo5juzdwp23hyianzg%252Fapp.bsky.feed.post%252F3mfemw4rrvs2t';
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
$this->assertGreaterThan(255, strlen($source));
|
||||
$this->assertDatabaseHas('webmentions', [
|
||||
|
|
@ -238,11 +223,9 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
<div class="e-content">Reposting someone else</div>
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/reply/1';
|
||||
|
|
@ -257,7 +240,7 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
]);
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
$this->assertDatabaseMissing('webmentions', [
|
||||
'source' => $source,
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Jobs;
|
||||
|
||||
use App\Jobs\SaveProfileImage;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
|
@ -58,12 +55,9 @@ class SaveProfileImageJobTest extends TestCase
|
|||
#[Test]
|
||||
public function remote_author_images_are_saved_locally(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
||||
Http::fake([
|
||||
'*' => Http::response('fake jpeg image', 200, ['Content-Type' => 'image/jpeg']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$mf = ['items' => []];
|
||||
$author = [
|
||||
'properties' => [
|
||||
|
|
@ -83,12 +77,9 @@ class SaveProfileImageJobTest extends TestCase
|
|||
#[Test]
|
||||
public function local_default_author_image_is_used_as_fallback(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(404),
|
||||
Http::fake([
|
||||
'*' => Http::response('', 404),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$mf = ['items' => []];
|
||||
$author = [
|
||||
'properties' => [
|
||||
|
|
@ -111,12 +102,9 @@ class SaveProfileImageJobTest extends TestCase
|
|||
#[Test]
|
||||
public function we_get_url_from_photo_object_if_alt_text_is_provided(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
||||
Http::fake([
|
||||
'*' => Http::response('fake jpeg image', 200, ['Content-Type' => 'image/jpeg']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$mf = ['items' => []];
|
||||
$author = [
|
||||
'properties' => [
|
||||
|
|
@ -139,12 +127,9 @@ class SaveProfileImageJobTest extends TestCase
|
|||
#[Test]
|
||||
public function use_first_url_if_multiple_homepages_are_provided(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
||||
Http::fake([
|
||||
'*' => Http::response('fake jpeg image', 200, ['Content-Type' => 'image/jpeg']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$mf = ['items' => []];
|
||||
$author = [
|
||||
'properties' => [
|
||||
|
|
|
|||
|
|
@ -6,13 +6,8 @@ namespace Tests\Unit\Jobs;
|
|||
|
||||
use App\Jobs\SaveScreenshot;
|
||||
use App\Models\Bookmark;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
|
@ -25,57 +20,34 @@ class SaveScreenshotJobTest extends TestCase
|
|||
public function screenshot_is_saved_by_job(): void
|
||||
{
|
||||
Storage::fake('public');
|
||||
$guzzleMock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], '{"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"finished","credits":null,"code":null,"message":null,"percent":100,"operation":"capture-website","engine":"chrome","engine_version":"107","result":null,"created_at":"2023-01-07T21:05:48+00:00","started_at":null,"ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":null,"storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
||||
new Response(201, ['Content-Type' => 'application/json'], '{"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"finished","credits":null,"code":null,"message":null,"percent":100,"operation":"export\/url","result":null,"created_at":"2023-01-07T21:10:02+00:00","started_at":null,"ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":null,"storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
||||
new Response(200, ['Content-Type' => 'image/png'], fopen(__DIR__.'/../../theverge.com.png', 'rb')),
|
||||
|
||||
Http::fake([
|
||||
'api.cloudconvert.com/v2/capture-website' => Http::response([
|
||||
'data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'finished'],
|
||||
], 201),
|
||||
'api.cloudconvert.com/v2/tasks/68d52633-e170-465e-b13e-746c97d01ffb*' => Http::response([
|
||||
'data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'finished'],
|
||||
], 200),
|
||||
'api.cloudconvert.com/v2/export/url' => Http::response([
|
||||
'data' => ['id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb', 'status' => 'finished'],
|
||||
], 201),
|
||||
'api.cloudconvert.com/v2/tasks/27f33137-cc03-4468-aba4-1e1aa8c096fb*' => Http::response([
|
||||
'data' => [
|
||||
'id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb',
|
||||
'status' => 'finished',
|
||||
'result' => [
|
||||
'files' => [[
|
||||
'url' => 'https://storage.cloudconvert.com/tasks/27f33137-cc03-4468-aba4-1e1aa8c096fb/theverge.com.png',
|
||||
]],
|
||||
],
|
||||
],
|
||||
], 200),
|
||||
'storage.cloudconvert.com/*' => Http::response(
|
||||
file_get_contents(__DIR__.'/../../theverge.com.png'),
|
||||
200,
|
||||
['Content-Type' => 'image/png']
|
||||
),
|
||||
]);
|
||||
$guzzleHandler = HandlerStack::create($guzzleMock);
|
||||
$guzzleClient = new Client(['handler' => $guzzleHandler]);
|
||||
$this->app->instance(Client::class, $guzzleClient);
|
||||
$retryMock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], '{"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"finished","credits":1,"code":null,"message":null,"percent":100,"operation":"capture-website","engine":"chrome","engine_version":"107","payload":{"url":"https:\/\/theverge.com","output_format":"png","screen_width":1440,"screen_height":900,"wait_until":"networkidle0","wait_time":"100"},"result":{"files":[{"filename":"theverge.com.png","size":811819}]},"created_at":"2023-01-07T21:05:48+00:00","started_at":"2023-01-07T21:05:48+00:00","ended_at":"2023-01-07T21:05:55+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
||||
new Response(200, ['Content-Type' => 'application/json'], '{"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"finished","credits":0,"code":null,"message":null,"percent":100,"operation":"export\/url","payload":{"input":"68d52633-e170-465e-b13e-746c97d01ffb","archive_multiple_files":false},"result":{"files":[{"filename":"theverge.com.png","size":811819,"url":"https:\/\/storage.cloudconvert.com\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb\/theverge.com.png?AWSAccessKeyId=cloudconvert-production&Expires=1673212203&Signature=xyz&response-content-disposition=attachment%3B%20filename%3D%22theverge.com.png%22&response-content-type=image%2Fpng"}]},"created_at":"2023-01-07T21:10:02+00:00","started_at":"2023-01-07T21:10:03+00:00","ended_at":"2023-01-07T21:10:03+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
||||
]);
|
||||
$retryHandler = HandlerStack::create($retryMock);
|
||||
$retryHandler->push(Middleware::retry(
|
||||
function ($retries, $request, $response, $exception) {
|
||||
// Limit the number of retries to 5
|
||||
if ($retries >= 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retry connection exceptions
|
||||
if ($exception instanceof ConnectException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retry on server errors
|
||||
if ($response && $response->getStatusCode() >= 500) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$responseBody = '';
|
||||
|
||||
if (is_string($response)) {
|
||||
$responseBody = $response;
|
||||
}
|
||||
|
||||
if ($response instanceof Response) {
|
||||
$responseBody = $response->getBody()->getContents();
|
||||
$response->getBody()->rewind();
|
||||
}
|
||||
|
||||
// Finally for CloudConvert, retry if status is not final
|
||||
return json_decode($responseBody, false, 512, JSON_THROW_ON_ERROR)?->data?->status !== 'finished';
|
||||
},
|
||||
function () {
|
||||
// Retry after 1 second
|
||||
return 1000;
|
||||
}
|
||||
));
|
||||
$retryClient = new Client(['handler' => $retryHandler]);
|
||||
$this->app->instance('RetryGuzzle', $retryClient);
|
||||
|
||||
$bookmark = Bookmark::factory()->create();
|
||||
$job = new SaveScreenshot($bookmark);
|
||||
|
|
@ -84,68 +56,45 @@ class SaveScreenshotJobTest extends TestCase
|
|||
|
||||
$this->assertEquals('68d52633-e170-465e-b13e-746c97d01ffb', $bookmark->screenshot);
|
||||
Storage::disk('public')->assertExists('/assets/img/bookmarks/'.$bookmark->screenshot.'.png');
|
||||
|
||||
// capture-website, 1x poll (finished immediately), export/url, 1x poll (finished immediately), download
|
||||
Http::assertSentCount(5);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function screenshot_job_handles_unfinished_tasks(): void
|
||||
{
|
||||
Storage::fake('public');
|
||||
$guzzleMock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], '{"id":1,"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"waiting","credits":null,"code":null,"message":null,"percent":100,"operation":"capture-website","engine":"chrome","engine_version":"107","result":null,"created_at":"2023-01-07T21:05:48+00:00","started_at":null,"ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":null,"storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
||||
new Response(201, ['Content-Type' => 'application/json'], '{"id":2,"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"waiting","credits":null,"code":null,"message":null,"percent":100,"operation":"export\/url","result":null,"created_at":"2023-01-07T21:10:02+00:00","started_at":null,"ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":null,"storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
||||
new Response(200, ['Content-Type' => 'image/png'], fopen(__DIR__.'/../../theverge.com.png', 'rb')),
|
||||
|
||||
Http::fake([
|
||||
'api.cloudconvert.com/v2/capture-website' => Http::response([
|
||||
'data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'waiting'],
|
||||
], 201),
|
||||
'api.cloudconvert.com/v2/tasks/68d52633-e170-465e-b13e-746c97d01ffb*' => Http::sequence()
|
||||
->push(['data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'waiting']], 200)
|
||||
->push(['data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'finished']], 200),
|
||||
'api.cloudconvert.com/v2/export/url' => Http::response([
|
||||
'data' => ['id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb', 'status' => 'waiting'],
|
||||
], 201),
|
||||
'api.cloudconvert.com/v2/tasks/27f33137-cc03-4468-aba4-1e1aa8c096fb*' => Http::sequence()
|
||||
->push(['data' => ['id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb', 'status' => 'waiting']], 200)
|
||||
->push([
|
||||
'data' => [
|
||||
'id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb',
|
||||
'status' => 'finished',
|
||||
'result' => [
|
||||
'files' => [[
|
||||
'url' => 'https://storage.cloudconvert.com/tasks/27f33137-cc03-4468-aba4-1e1aa8c096fb/theverge.com.png',
|
||||
]],
|
||||
],
|
||||
],
|
||||
], 200),
|
||||
'storage.cloudconvert.com/*' => Http::response(
|
||||
file_get_contents(__DIR__.'/../../theverge.com.png'),
|
||||
200,
|
||||
['Content-Type' => 'image/png']
|
||||
),
|
||||
]);
|
||||
$guzzleHandler = HandlerStack::create($guzzleMock);
|
||||
$guzzleClient = new Client(['handler' => $guzzleHandler]);
|
||||
$this->app->instance(Client::class, $guzzleClient);
|
||||
$container = [];
|
||||
$history = Middleware::history($container);
|
||||
$retryMock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], '{"id":3,"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"waiting","credits":1,"code":null,"message":null,"percent":50,"operation":"capture-website","engine":"chrome","engine_version":"107","payload":{"url":"https:\/\/theverge.com","output_format":"png","screen_width":1440,"screen_height":900,"wait_until":"networkidle0","wait_time":"100"},"result":{"files":[{"filename":"theverge.com.png","size":811819}]},"created_at":"2023-01-07T21:05:48+00:00","started_at":"2023-01-07T21:05:48+00:00","ended_at":"2023-01-07T21:05:55+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
||||
new Response(200, ['Content-Type' => 'application/json'], '{"id":4,"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"finished","credits":1,"code":null,"message":null,"percent":100,"operation":"capture-website","engine":"chrome","engine_version":"107","payload":{"url":"https:\/\/theverge.com","output_format":"png","screen_width":1440,"screen_height":900,"wait_until":"networkidle0","wait_time":"100"},"result":{"files":[{"filename":"theverge.com.png","size":811819}]},"created_at":"2023-01-07T21:05:48+00:00","started_at":"2023-01-07T21:05:48+00:00","ended_at":"2023-01-07T21:05:55+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
||||
new Response(200, ['Content-Type' => 'application/json'], '{"id":5,"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"waiting","credits":0,"code":null,"message":null,"percent":50,"operation":"export\/url","payload":{"input":"68d52633-e170-465e-b13e-746c97d01ffb","archive_multiple_files":false},"created_at":"2023-01-07T21:10:02+00:00","started_at":"2023-01-07T21:10:03+00:00","ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
||||
new Response(200, ['Content-Type' => 'application/json'], '{"id":6,"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"finished","credits":0,"code":null,"message":null,"percent":100,"operation":"export\/url","payload":{"input":"68d52633-e170-465e-b13e-746c97d01ffb","archive_multiple_files":false},"result":{"files":[{"filename":"theverge.com.png","size":811819,"url":"https:\/\/storage.cloudconvert.com\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb\/theverge.com.png?AWSAccessKeyId=cloudconvert-production&Expires=1673212203&Signature=xyz&response-content-disposition=attachment%3B%20filename%3D%22theverge.com.png%22&response-content-type=image%2Fpng"}]},"created_at":"2023-01-07T21:10:02+00:00","started_at":"2023-01-07T21:10:03+00:00","ended_at":"2023-01-07T21:10:03+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
||||
]);
|
||||
$retryHandler = HandlerStack::create($retryMock);
|
||||
$retryHandler->push($history);
|
||||
$retryHandler->push(Middleware::retry(
|
||||
function ($retries, $request, $response, $exception) {
|
||||
// Limit the number of retries to 5
|
||||
if ($retries >= 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retry connection exceptions
|
||||
if ($exception instanceof ConnectException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retry on server errors
|
||||
if ($response && $response->getStatusCode() >= 500) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$responseBody = '';
|
||||
|
||||
if (is_string($response)) {
|
||||
$responseBody = $response;
|
||||
}
|
||||
|
||||
if ($response instanceof Response) {
|
||||
$responseBody = $response->getBody()->getContents();
|
||||
$response->getBody()->rewind();
|
||||
}
|
||||
|
||||
// Finally for CloudConvert, retry if status is not final
|
||||
return json_decode($responseBody, false, 512, JSON_THROW_ON_ERROR)?->data?->status !== 'finished';
|
||||
},
|
||||
function () {
|
||||
// Retry after 1 second
|
||||
return 1000;
|
||||
}
|
||||
));
|
||||
$retryClient = new Client(['handler' => $retryHandler]);
|
||||
$this->app->instance('RetryGuzzle', $retryClient);
|
||||
|
||||
$bookmark = Bookmark::factory()->create();
|
||||
$job = new SaveScreenshot($bookmark);
|
||||
|
|
@ -154,9 +103,10 @@ class SaveScreenshotJobTest extends TestCase
|
|||
|
||||
$this->assertEquals('68d52633-e170-465e-b13e-746c97d01ffb', $bookmark->screenshot);
|
||||
Storage::disk('public')->assertExists('/assets/img/bookmarks/'.$bookmark->screenshot.'.png');
|
||||
// Also assert we made the correct number of requests
|
||||
$this->assertCount(2, $container);
|
||||
// However with retries there should be more than 4 responses for the 2 requests
|
||||
$this->assertEquals(0, $retryMock->count());
|
||||
|
||||
// capture-website, 2x poll (waiting then finished), export/url, 2x poll (waiting then finished), download
|
||||
Http::assertSentCount(7);
|
||||
// Also assert every queued response in each sequence was consumed, no more no less
|
||||
Http::assertSequencesAreEmpty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ namespace Tests\Unit\Jobs;
|
|||
|
||||
use App\Jobs\SendWebMentions;
|
||||
use App\Models\Note;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -28,12 +25,9 @@ class SendWebMentionJobTest extends TestCase
|
|||
public function discover_webmention_endpoint_from_header_links(): void
|
||||
{
|
||||
$url = 'https://example.org/webmention';
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Link' => '<'.$url.'>; rel="webmention"']),
|
||||
Http::fake([
|
||||
'*' => Http::response('', 200, ['Link' => '<'.$url.'>; rel="webmention"']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$job = new SendWebMentions(new Note);
|
||||
$this->assertEquals($url, $job->discoverWebmentionEndpoint('https://example.org'));
|
||||
|
|
@ -43,12 +37,9 @@ class SendWebMentionJobTest extends TestCase
|
|||
public function discover_webmention_endpoint_from_html_link_tags(): void
|
||||
{
|
||||
$html = '<link rel="webmention" href="https://example.org/webmention">';
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$job = new SendWebMentions(new Note);
|
||||
$this->assertEquals(
|
||||
|
|
@ -61,12 +52,9 @@ class SendWebMentionJobTest extends TestCase
|
|||
public function discover_webmention_endpoint_from_legacy_html_markup(): void
|
||||
{
|
||||
$html = '<link rel="http://webmention.org/" href="https://example.org/webmention">';
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$job = new SendWebMentions(new Note);
|
||||
$this->assertEquals(
|
||||
|
|
@ -95,13 +83,10 @@ class SendWebMentionJobTest extends TestCase
|
|||
public function we_send_a_webmention_for_a_note(): void
|
||||
{
|
||||
$html = '<link rel="http://webmention.org/" href="https://example.org/webmention">';
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
new Response(202),
|
||||
Http::fake([
|
||||
'example.org/webmention' => Http::response('', 202),
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$note = new Note;
|
||||
$note->note = 'Hi [Aaron](https://aaronparecki.com)';
|
||||
|
|
@ -114,13 +99,10 @@ class SendWebMentionJobTest extends TestCase
|
|||
#[Test]
|
||||
public function links_in_notes_can_not_support_webmentions(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
Http::fake([
|
||||
// URLs with commas currently break the parse function I’m using
|
||||
new Response(200, ['Link' => '<https://example.org/foo,bar>; rel="preconnect"']),
|
||||
'*' => Http::response('', 200, ['Link' => '<https://example.org/foo,bar>; rel="preconnect"']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$job = new SendWebMentions(new Note);
|
||||
$this->assertNull($job->discoverWebmentionEndpoint('https://example.org'));
|
||||
|
|
|
|||
|
|
@ -5,12 +5,9 @@ namespace Tests\Unit\Jobs;
|
|||
use App\Jobs\SyndicateNoteToBluesky;
|
||||
use App\Models\Note;
|
||||
use Faker\Factory;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\Client\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -24,19 +21,17 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
|||
$faker = Factory::create();
|
||||
$randomNumber = $faker->randomNumber();
|
||||
$blueskyUrl = 'https://bsky.app/profile/jonnybarnes.uk/'.$randomNumber;
|
||||
$mock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'url' => $blueskyUrl,
|
||||
'id' => (string) $randomNumber,
|
||||
'type' => ['h-entry'],
|
||||
])),
|
||||
], 201),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$job = new SyndicateNoteToBluesky($note);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertDatabaseHas('notes', [
|
||||
'bluesky_url' => $blueskyUrl,
|
||||
|
|
@ -46,39 +41,31 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
|||
#[Test]
|
||||
public function we_post_the_correct_source_and_target(): void
|
||||
{
|
||||
$container = [];
|
||||
$history = Middleware::history($container);
|
||||
$mock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'url' => 'https://bsky.app/profile/jonnybarnes.uk/1',
|
||||
])),
|
||||
], 201),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$handler->push($history);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create(['note' => 'This is a **test**']);
|
||||
$job = new SyndicateNoteToBluesky($note);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$request = $container[0]['request'];
|
||||
$body = [];
|
||||
parse_str((string) $request->getBody(), $body);
|
||||
|
||||
$this->assertSame('https://brid.gy/publish/webmention', (string) $request->getUri());
|
||||
$this->assertSame($note->uri, $body['source']);
|
||||
$this->assertSame('https://brid.gy/publish/bluesky', $body['target']);
|
||||
Http::assertSent(function (Request $request) use ($note) {
|
||||
return $request->url() === 'https://brid.gy/publish/webmention'
|
||||
&& $request['source'] === $note->uri
|
||||
&& $request['target'] === 'https://brid.gy/publish/bluesky';
|
||||
});
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_bridgy_failure_throws_and_does_not_set_bluesky_url(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(400, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'error' => 'Could not find target link',
|
||||
])),
|
||||
], 400),
|
||||
]);
|
||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$job = new SyndicateNoteToBluesky($note);
|
||||
|
|
@ -86,7 +73,7 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
|||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
try {
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
} finally {
|
||||
$this->assertDatabaseHas('notes', [
|
||||
'id' => $note->id,
|
||||
|
|
|
|||
|
|
@ -5,12 +5,9 @@ namespace Tests\Unit\Jobs;
|
|||
use App\Jobs\SyndicateNoteToMastodon;
|
||||
use App\Models\Note;
|
||||
use Faker\Factory;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\Client\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -24,19 +21,17 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
|||
$faker = Factory::create();
|
||||
$randomNumber = $faker->randomNumber();
|
||||
$mastodonUrl = 'https://mastodon.example/@jonny/'.$randomNumber;
|
||||
$mock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'url' => $mastodonUrl,
|
||||
'id' => (string) $randomNumber,
|
||||
'type' => ['h-entry'],
|
||||
])),
|
||||
], 201),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$job = new SyndicateNoteToMastodon($note);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertDatabaseHas('notes', [
|
||||
'mastodon_url' => $mastodonUrl,
|
||||
|
|
@ -46,39 +41,31 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
|||
#[Test]
|
||||
public function we_post_the_correct_source_and_target(): void
|
||||
{
|
||||
$container = [];
|
||||
$history = Middleware::history($container);
|
||||
$mock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'url' => 'https://mastodon.example/@jonny/1',
|
||||
])),
|
||||
], 201),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$handler->push($history);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create(['note' => 'This is a **test**']);
|
||||
$job = new SyndicateNoteToMastodon($note);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$request = $container[0]['request'];
|
||||
$body = [];
|
||||
parse_str((string) $request->getBody(), $body);
|
||||
|
||||
$this->assertSame('https://brid.gy/publish/webmention', (string) $request->getUri());
|
||||
$this->assertSame($note->uri, $body['source']);
|
||||
$this->assertSame('https://brid.gy/publish/mastodon', $body['target']);
|
||||
Http::assertSent(function (Request $request) use ($note) {
|
||||
return $request->url() === 'https://brid.gy/publish/webmention'
|
||||
&& $request['source'] === $note->uri
|
||||
&& $request['target'] === 'https://brid.gy/publish/mastodon';
|
||||
});
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_bridgy_failure_throws_and_does_not_set_mastodon_url(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(400, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'error' => 'Could not find target link',
|
||||
])),
|
||||
], 400),
|
||||
]);
|
||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$job = new SyndicateNoteToMastodon($note);
|
||||
|
|
@ -86,7 +73,7 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
|||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
try {
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
} finally {
|
||||
$this->assertDatabaseHas('notes', [
|
||||
'id' => $note->id,
|
||||
|
|
|
|||
|
|
@ -9,13 +9,10 @@ use App\Models\Media;
|
|||
use App\Models\Note;
|
||||
use App\Models\Place;
|
||||
use App\Models\Tag;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -182,13 +179,9 @@ class NotesTest extends TestCase
|
|||
{"place_id":"198791063","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"relation","osm_id":"5208404","lat":"51.50084125","lon":"-0.142990166340849","display_name":"Buckingham Palace, Ambassador's Court, St. James's, Victoria, Westminster, London, Greater London, England, SW1E 6LA, United Kingdom","address":{"attraction":"Buckingham Palace","road":"Ambassador's Court","neighbourhood":"St. James's","suburb":"Victoria","city":"London","state_district":"Greater London","state":"England","postcode":"SW1E 6LA","country":"UK","country_code":"gb"},"boundingbox":["51.4997342","51.5019473","-0.143984","-0.1413002"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$note = new Note;
|
||||
$address = $note->reverseGeoCode(51.50084, -0.14264);
|
||||
|
|
@ -207,13 +200,9 @@ class NotesTest extends TestCase
|
|||
{"place_id":"96518506","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"94107885","lat":"51.0225764535969","lon":"0.906664040464189","display_name":"Melon Lane, Newchurch, Shepway, Kent, South East, England, TN29 0AS, United Kingdom","address":{"road":"Melon Lane","suburb":"Newchurch","city":"Shepway","county":"Kent","state_district":"South East","state":"England","postcode":"TN29 0AS","country":"UK","country_code":"gb"},"boundingbox":["51.0140377","51.0371494","0.8873312","0.9109506"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$note = new Note;
|
||||
$address = $note->reverseGeoCode(51.02, 0.91);
|
||||
|
|
@ -234,13 +223,9 @@ class NotesTest extends TestCase
|
|||
{"place_id":"198561071","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"relation","osm_id":"1839026","lat":"53.46600455","lon":"-2.23300880782987","display_name":"University of Manchester - Main Campus, Brunswick Street, Curry Mile, Ardwick, Manchester, Greater Manchester, North West England, England, M13 9NR, United Kingdom","address":{"university":"University of Manchester - Main Campus","city":"Manchester","county":"Greater Manchester","state_district":"North West England","state":"England","postcode":"M13 9NR","country":"UK","country_code":"gb"},"boundingbox":["53.4598667","53.4716848","-2.2390346","-2.2262754"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$note = new Note;
|
||||
$address = $note->reverseGeoCode(53.466277988406, -2.2304474827445);
|
||||
|
|
@ -261,13 +246,9 @@ class NotesTest extends TestCase
|
|||
{"place_id":"98085404","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"103703318","lat":"51.0997470194065","lon":"0.609897771085209","display_name":"Biddenden, Ashford, Kent, South East, England, TN27 8ET, United Kingdom","address":{"county":"Kent","state_district":"South East","state":"England","postcode":"TN27 8ET","country":"UK","country_code":"gb"},"boundingbox":["51.0986632","51.104459","0.5954434","0.6167775"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$note = new Note;
|
||||
$address = $note->reverseGeoCode(51.1, 0.61);
|
||||
|
|
@ -285,13 +266,9 @@ class NotesTest extends TestCase
|
|||
{"place_id":"120553244","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"191508282","lat":"54.3004150140189","lon":"-9.39993720828084","display_name":"R314, Doonfeeny Lower, Ballycastle ED, Ballina, County Mayo, Connacht, Ireland","address":{"country":"Ireland","country_code":"ie"},"boundingbox":["54.2964027","54.3045856","-9.4337961","-9.3960403"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$note = new Note;
|
||||
$address = $note->reverseGeoCode(54.3, 9.4);
|
||||
|
|
|
|||
Loading…
Reference in a new issue