From 0a053c0f27ed55aab6ac8b4eaea14762b5b564dd Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sun, 10 Nov 2024 14:29:42 +0700 Subject: [PATCH] chore: enhance error handling and refactor URL processing logic --- .../BatchLinkPreviewController.php | 135 ++++++++++++------ 1 file changed, 88 insertions(+), 47 deletions(-) diff --git a/src/Api/Controllers/BatchLinkPreviewController.php b/src/Api/Controllers/BatchLinkPreviewController.php index 534869e..5e2eb4f 100644 --- a/src/Api/Controllers/BatchLinkPreviewController.php +++ b/src/Api/Controllers/BatchLinkPreviewController.php @@ -16,78 +16,119 @@ public function __construct(protected LinkPreviewService $service) {} public function handle(Request $request): Response { - $urls = $request->getParsedBody()['urls'] ?? []; - $urlsToFetch = []; + try { + $urls = $request->getParsedBody()['urls'] ?? []; + $result = $this->processUrls($urls); + + return new JsonResponse($result); + } catch (Throwable $e) { + return new JsonResponse([ + 'error' => $e->getMessage(), + ]); + } + } + + protected function processUrls(array $urls): array + { $result = []; + $urlsToFetch = []; foreach ($urls as $url) { - if (!$this->service->isValidUrl($url)) { - $result[$url] = $this->service->getErrorResponse('datlechin-link-preview.forum.site_cannot_be_reached'); - continue; + $processResult = $this->preProcessUrl($url); + if (isset($processResult['data'])) { + $result[$url] = $processResult['data']; + } else if (isset($processResult['fetch'])) { + $urlsToFetch[$url] = $processResult['fetch']; + } else { + $result[$url] = $processResult['error']; } + } - $normalizedUrl = $this->service->normalizeUrl($url); + if (empty($urlsToFetch)) { + return $result; + } - if (!$this->service->isUrlAllowed($normalizedUrl)) { - $result[$url] = $this->service->getErrorResponse('datlechin-link-preview.forum.site_cannot_be_reached'); - continue; - } + $fetchResults = $this->fetchUrls($urlsToFetch); - $cachedData = $this->service->getCachedData($normalizedUrl); - if ($cachedData) { - $result[$url] = $cachedData; - continue; - } + foreach ($urlsToFetch as $originalUrl => $normalizedUrl) { + $result[$originalUrl] = $fetchResults[$originalUrl] ?? [ + 'error' => 'Failed to fetch preview' + ]; + } + + return $result; + } - $urlsToFetch[$url] = $normalizedUrl; + protected function preProcessUrl(string $url): array + { + if (!$this->service->isValidUrl($url)) { + return [ + 'error' => $this->service->getErrorResponse('datlechin-link-preview.forum.site_cannot_be_reached') + ]; } - if (empty($urlsToFetch)) { - return new JsonResponse($result); + $normalizedUrl = $this->service->normalizeUrl($url); + + if (!$this->service->isUrlAllowed($normalizedUrl)) { + return [ + 'error' => $this->service->getErrorResponse('datlechin-link-preview.forum.site_cannot_be_reached') + ]; + } + + $cachedData = $this->service->getCachedData($normalizedUrl); + if ($cachedData) { + return ['data' => $cachedData]; + } + + return ['fetch' => $normalizedUrl]; + } + + protected function fetchUrls(array $urlsToFetch): array + { + $promises = []; + $results = []; + + foreach ($urlsToFetch as $originalUrl) { + try { + $promises[$originalUrl] = $this->service->getClient()->getAsync($originalUrl); + } catch (Throwable $e) { + $results[$originalUrl] = [ + 'error' => 'Failed to create request: ' . $e->getMessage() + ]; + } } - $promises = $this->preparePromises($urlsToFetch); + if (empty($promises)) { + return $results; + } $responses = Utils::settle($promises)->wait(); - foreach ($responses as $originalUrl => $response) { + foreach ($promises as $originalUrl => $promise) { try { - if (!isset($responses[$originalUrl])) { - $result[$originalUrl] = [ - 'error' => 'No response received', + $response = $responses[$originalUrl] ?? null; + + if (!$response || $response['state'] !== 'fulfilled') { + $results[$originalUrl] = [ + 'error' => $response['reason'] instanceof \Exception ? + $response['reason']->getMessage() : + 'Failed to fetch preview' ]; continue; } - if ($response['state'] === 'fulfilled') { - $html = $response['value']->getBody()->getContents(); - $data = $this->service->parseHtml($html, $originalUrl); + $html = $response['value']->getBody()->getContents(); + $data = $this->service->parseHtml($html, $originalUrl); - $this->service->cacheData($urlsToFetch[$originalUrl], $data); - $result[$originalUrl] = $data; - } else { - $result[$originalUrl] = [ - 'error' => $response['reason']->getMessage(), - ]; - } + $this->service->cacheData($urlsToFetch[$originalUrl], $data); + $results[$originalUrl] = $data; } catch (Throwable $e) { - $result[$originalUrl] = [ - 'error' => $e->getMessage(), + $results[$originalUrl] = [ + 'error' => $e->getMessage() ]; } } - return new JsonResponse($result); - } - - protected function preparePromises(array $urlsToFetch): array - { - $promises = []; - - foreach ($urlsToFetch as $originalUrl) { - $promises[$originalUrl] = $this->service->getClient()->getAsync($originalUrl); - } - - return $promises; + return $results; } }