Skip to content

Commit

Permalink
Receive parsed keywords instead of fetched HTML for tag suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
Kovah committed Nov 1, 2023
1 parent 31331bc commit 3dc4a3d
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 39 deletions.
20 changes: 16 additions & 4 deletions app/Http/Controllers/FetchController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Http;
use Masterminds\HTML5;

class FetchController extends Controller
{
Expand Down Expand Up @@ -121,9 +122,9 @@ public static function checkForUpdates(): JsonResponse
* implementation.
*
* @param Request $request
* @return Response
* @return JsonResponse
*/
public function htmlForUrl(Request $request): Response
public function htmlKeywordsFromUrl(Request $request)
{
$request->validate([
'url' => ['url'],
Expand All @@ -138,9 +139,20 @@ public function htmlForUrl(Request $request): Response
$response = $newRequest->get($url);

if ($response->successful()) {
return response($response->body());
$html5 = new HTML5();
$dom = $html5->loadHTML($response->body());
$keywords = [];
/** @var \DOMElement $metaTag */
foreach ($dom->getElementsByTagName('meta') as $metaTag) {
if (strtolower($metaTag->getAttribute('name')) === 'keywords') {
$keywords = explode(',', $metaTag->getAttributeNode('content')?->value);
$keywords = array_map(fn($keyword) => trim(e($keyword)), $keywords);
array_push($keywords, ...$keywords);
}
}
return response()->json(['keywords' => $keywords]);
}

return response(null);
return response()->json(['keywords' => null]);
}
}
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"league/flysystem-aws-s3-v3": "^3.0",
"league/flysystem-ftp": "^3.0",
"league/flysystem-sftp-v3": "^3.0",
"masterminds/html5": "^2.8",
"predis/predis": "^v2.1",
"rap2hpoutre/laravel-log-viewer": "^v2.2.0",
"sentry/sentry-laravel": "^3.3.0",
Expand Down
69 changes: 68 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions resources/assets/js/components/TagsSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ export default class TagsSelect {

this.$suggestionsContent.innerHTML = '';

tags.forEach(newTag => {
newTag = newTag.trim();

tags.slice(0, 20).forEach(newTag => {
const $tag = document.createElement('span');
$tag.classList.add('btn', 'btn-outline-secondary', 'btn-xs');
$tag.innerText = newTag;
Expand Down
24 changes: 5 additions & 19 deletions resources/assets/js/components/UrlField.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default class UrlField {
return;
}

fetch(window.appData.routes.fetch.htmlForUrl, {
fetch(window.appData.routes.fetch.keywordsForUrl, {
method: 'POST',
credentials: 'same-origin',
headers: {'Content-Type': 'application/json'},
Expand All @@ -85,25 +85,11 @@ export default class UrlField {
url: url
})
})
.then(response => response.text())
.then(body => {
if (body !== null) {
this.parseHtmlForKeywords(body);
.then(response => response.json())
.then(data => {
if (data.keywords !== null) {
this.tagSuggestions.displayNewSuggestions(data.keywords);
}
});
}

parseHtmlForKeywords (body) {
const parser = new DOMParser();
const doc = parser.parseFromString(body, 'text/html');

if (doc.head.children.length > 0) {
const keywords = doc.head.children.namedItem('keywords')
|| doc.head.children.namedItem('Keywords');

if (keywords !== null && keywords.content.length > 0) {
this.tagSuggestions.displayNewSuggestions(keywords.content.split(','));
}
}
}
}
2 changes: 1 addition & 1 deletion resources/views/partials/header.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
'searchLists' => route('fetch-lists'),
'searchTags' => route('fetch-tags'),
'existingLinks' => route('fetch-existing-links'),
'htmlForUrl' => route('fetch-html-for-url'),
'keywordsForUrl' => route('fetch-keywords-for-url'),
'updateCheck' => route('fetch-update-check'),
'generateApiToken' => route('generate-api-token'),
'generateCronToken' => route('generate-cron-token'),
Expand Down
4 changes: 2 additions & 2 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@
->name('fetch-lists');
Route::post('fetch/existing-links', [FetchController::class, 'searchExistingUrls'])
->name('fetch-existing-links');
Route::post('fetch/html-for-url', [FetchController::class, 'htmlForUrl'])
->name('fetch-html-for-url');
Route::post('fetch/keywords-for-url', [FetchController::class, 'htmlKeywordsFromUrl'])
->name('fetch-keywords-for-url');
Route::get('fetch/update-check', [FetchController::class, 'checkForUpdates'])
->name('fetch-update-check');

Expand Down
21 changes: 12 additions & 9 deletions tests/Controller/FetchControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,46 +82,49 @@ public function testExistingUrlSearchWithoutResult(): void
$response->assertOk()->assertJson(['linkFound' => false]);
}

public function testGetHtmlForUrl(): void
public function testGetHtmlKeywordsForUrl(): void
{
$testHtml = '<!DOCTYPE html><head>' .
'<title>Example Title</title>' .
'<meta name="description" content="This an example description">' .
'<meta name="keywords" content="html, css, javascript">' .
'</head></html>';

Http::fake([
'example.com' => Http::response($testHtml, 200),
]);

$response = $this->post('fetch/html-for-url', [
$response = $this->post('fetch/keywords-for-url', [
'url' => 'https://example.com',
]);

$response->assertOk();

$responseHtml = $response->content();
$this->assertEquals($testHtml, $responseHtml);
$keywords = $response->json('keywords');
$this->assertEquals('html', $keywords[0]);
$this->assertEquals('css', $keywords[1]);
$this->assertEquals('javascript', $keywords[2]);
}

public function testGetHtmlForInvalidUrl(): void
public function testGetKeywordsForInvalidUrl(): void
{
$response = $this->post('fetch/html-for-url', [
$response = $this->post('fetch/keywords-for-url', [
'url' => 'not a url',
]);

$response->assertSessionHasErrors('url');
}

public function testGetHtmlForUrlWithFailure(): void
public function testGetKeywordsForUrlWithFailure(): void
{
Http::fake([
'example.com' => Http::response('', 500),
]);

$response = $this->post('fetch/html-for-url', [
$response = $this->post('fetch/keywords-for-url', [
'url' => 'https://example.com',
]);

$response->assertOk()->assertSee('');
$response->assertOk()->assertJson(['keywords' => null]);
}
}

0 comments on commit 3dc4a3d

Please sign in to comment.