From ee66127d27339d3b8591c9247555e8e57e2efb0a Mon Sep 17 00:00:00 2001 From: SupunKavinda Date: Mon, 21 Oct 2024 23:07:53 +0530 Subject: [PATCH 1/2] metadata priority --- src/Link/Metadata/MetadataObject.php | 13 +- src/Link/Metadata/MetadataPriority.php | 193 ++++++++++++++++++ src/Unfolded/Unfolded.php | 37 ++-- tests/Feature/UnfoldTest.php | 14 +- .../Metadata}/MetadataParserTest.php | 0 tests/Unit/Link/Metadata/MetadataPriority.php | 41 ++++ website/package-lock.json | 83 ++++---- website/package.json | 2 +- website/src/routes/+layout.ts | 1 + website/svelte.config.js | 2 +- 10 files changed, 316 insertions(+), 70 deletions(-) create mode 100644 src/Link/Metadata/MetadataPriority.php rename tests/Unit/{MetadataParsers => Link/Metadata}/MetadataParserTest.php (100%) create mode 100644 tests/Unit/Link/Metadata/MetadataPriority.php create mode 100644 website/src/routes/+layout.ts diff --git a/src/Link/Metadata/MetadataObject.php b/src/Link/Metadata/MetadataObject.php index 938b593..2f124f7 100644 --- a/src/Link/Metadata/MetadataObject.php +++ b/src/Link/Metadata/MetadataObject.php @@ -2,15 +2,20 @@ namespace Hyvor\Unfold\Link\Metadata; -use DateTimeInterface; -use Hyvor\Unfold\Unfolded\UnfoldedAuthor; -use Hyvor\Unfold\Unfolded\UnfoldedTag; +/** + * @template T extends string|DateTimeInterface|UnfoldedAuthor|UnfoldedTag + */ class MetadataObject { + + /** + * @param MetadataKeyType $key + * @param T $value + */ public function __construct( public MetadataKeyType $key, - public string|DateTimeInterface|UnfoldedAuthor|UnfoldedTag $value, + public $value, ) { } } diff --git a/src/Link/Metadata/MetadataPriority.php b/src/Link/Metadata/MetadataPriority.php new file mode 100644 index 0000000..853b702 --- /dev/null +++ b/src/Link/Metadata/MetadataPriority.php @@ -0,0 +1,193 @@ + [MetadataObject], + * 'TITLE' => [MetadataObject, MetadataObject], + * ] + */ + foreach ($this->metadata as $metadata) { + $key = $metadata->key; + if (!in_array($key, $keys)) { + continue; + } + + $keyName = $key->name; + if (isset($keyedMetadata[$keyName])) { + $keyedMetadata[$keyName][] = $metadata; + } else { + $keyedMetadata[$keyName] = [$metadata]; + } + } + + /** + * Next, we are sorting $keyedMetadata by the order of $keys + * If $keys was [TITLE, OG_TITLE], then the order of $keyedMetadata will be + * [ + * 'TITLE' => [MetadataObject, MetadataObject], + * 'OG_TITLE' => [MetadataObject], + * ] + */ + $keysNames = array_map(fn($key) => $key->name, $keys); + uksort($keyedMetadata, function ($a, $b) use ($keysNames) { + return array_search($a, $keysNames) - array_search($b, $keysNames); + }); + + // index by 0,1,2 + $keyedMetadata = array_values($keyedMetadata); + // return the values + return array_map(fn($metadata) => $metadata->value, $keyedMetadata[0] ?? []); + } + + /** + * @param MetadataKeyType[] $keys + * @return mixed|null + */ + private function prioritized(array $keys) + { + return $this->prioritizedAll($keys)[0] ?? null; + } + + public function title(): ?string + { + return $this->prioritized( + [ + MetadataKeyType::TITLE, + MetadataKeyType::OG_TITLE, + MetadataKeyType::TWITTER_TITLE + ] + ); + } + + public function description(): ?string + { + return $this->prioritized([ + MetadataKeyType::DESCRIPTION, + MetadataKeyType::OG_DESCRIPTION, + MetadataKeyType::TWITTER_DESCRIPTION + ]); + } + + /** + * @return UnfoldedAuthor[] + */ + public function authors() + { + return $this->prioritizedAll([ + MetadataKeyType::RICH_SCHEMA_AUTHOR, + MetadataKeyType::OG_ARTICLE_AUTHOR, + MetadataKeyType::TWITTER_CREATOR + ]); + } + + /** + * @return UnfoldedTag[] + */ + public function tags(): array + { + return $this->prioritizedAll([ + MetadataKeyType::OG_ARTICLE_TAG + ]); + } + + + public function siteName(): ?string + { + return $this->prioritized([ + MetadataKeyType::OG_SITE_NAME, + MetadataKeyType::TWITTER_SITE + ]); + } + + + public function siteUrl(): ?string + { + return $this->prioritized([ + MetadataKeyType::OG_URL + ]); + } + + + public function canonicalUrl(): ?string + { + return $this->prioritized([ + MetadataKeyType::CANONICAL_URL + ]); + } + + + public function publishedTime(): ?DateTimeInterface + { + return $this->prioritized([ + MetadataKeyType::RICH_SCHEMA_PUBLISHED_TIME, + MetadataKeyType::OG_ARTICLE_PUBLISHED_TIME + ]); + } + + + public function modifiedTime(): ?DateTimeInterface + { + return $this->prioritized([ + MetadataKeyType::RICH_SCHEMA_MODIFIED_TIME, + MetadataKeyType::OG_ARTICLE_MODIFIED_TIME + ]); + } + + + public function thumbnailUrl(): ?string + { + return $this->prioritized([ + MetadataKeyType::OG_IMAGE, + MetadataKeyType::OG_IMAGE_URL, + MetadataKeyType::OG_IMAGE_SECURE_URL, + MetadataKeyType::TWITTER_IMAGE + ]); + } + + + public function iconUrl(): ?string + { + return $this->prioritized([ + MetadataKeyType::FAVICON_URL + ]); + } + + + public function locale(): ?string + { + return $this->prioritized([ + MetadataKeyType::LOCALE, + MetadataKeyType::OG_LOCALE + ]); + } + +} \ No newline at end of file diff --git a/src/Unfolded/Unfolded.php b/src/Unfolded/Unfolded.php index b034b41..1e7f263 100644 --- a/src/Unfolded/Unfolded.php +++ b/src/Unfolded/Unfolded.php @@ -6,6 +6,7 @@ use Hyvor\Unfold\Embed\EmbedResponseObject; use Hyvor\Unfold\Link\Metadata\MetadataKeyType; use Hyvor\Unfold\Link\Metadata\MetadataObject; +use Hyvor\Unfold\Link\Metadata\MetadataPriority; use Hyvor\Unfold\UnfoldCallContext; use Hyvor\Unfold\UnfoldMethod; @@ -203,9 +204,9 @@ public static function getMetadataFromKeys( $value[] = $meta->value; $keyIndex = array_search($meta->key, $keys); // set the new key index } elseif ($isMultiple && ($keyIndex === array_search( - $meta->key, - $keys - ))) { // if multiple values are allowed and same priority key found + $meta->key, + $keys + ))) { // if multiple values are allowed and same priority key found $value[] = $meta->value; } } @@ -216,7 +217,7 @@ public static function getMetadataFromKeys( return $isMultiple ? $value : // return the array of values ( - count($value) !== 0 ? + count($value) !== 0 ? $value[0] : // return the first value null // return null if no value found ); @@ -229,23 +230,25 @@ public static function fromMetadata( string $url, array $metadata, UnfoldCallContext $context, - ) { + ): self { + $metadataPriority = new MetadataPriority($metadata); + return new self( $context->method, $url, null, - self::title($metadata), - self::description($metadata), - self::authors($metadata), - self::tags($metadata), - self::siteName($metadata), - self::siteUrl($metadata), - self::canonicalUrl($metadata), - self::publishedTime($metadata), - self::modifiedTime($metadata), - self::thumbnailUrl($metadata), - self::iconUrl($metadata), - self::locale($metadata), + $metadataPriority->title(), + $metadataPriority->description(), + $metadataPriority->authors(), + $metadataPriority->tags(), + $metadataPriority->siteName(), + $metadataPriority->siteUrl(), + $metadataPriority->canonicalUrl(), + $metadataPriority->publishedTime(), + $metadataPriority->modifiedTime(), + $metadataPriority->thumbnailUrl(), + $metadataPriority->iconUrl(), + $metadataPriority->locale(), $context->duration() ); } diff --git a/tests/Feature/UnfoldTest.php b/tests/Feature/UnfoldTest.php index 822154c..9d5e1a9 100644 --- a/tests/Feature/UnfoldTest.php +++ b/tests/Feature/UnfoldTest.php @@ -1,11 +1,17 @@ title(); + expect($title)->toBe('title'); +}); + +it('prioritized authors', function () { + $metadata = [ + new MetadataObject(MetadataKeyType::OG_ARTICLE_AUTHOR, 'Author1'), + new MetadataObject(MetadataKeyType::OG_ARTICLE_AUTHOR, 'Author2'), + new MetadataObject(MetadataKeyType::OG_ARTICLE_AUTHOR, 'Author3'), + // some tags + new MetadataObject(MetadataKeyType::OG_ARTICLE_TAG, 'Tag1'), + new MetadataObject(MetadataKeyType::OG_ARTICLE_TAG, 'Tag2'), + ]; + + $priority = new MetadataPriority($metadata); + $authors = $priority->authors(); + expect($authors)->toEqual([ + 'Author1', + 'Author2', + 'Author3', + ]); +}); + +it('with already correct sort', function () { +}); \ No newline at end of file diff --git a/website/package-lock.json b/website/package-lock.json index 6a23d78..7e359ab 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -12,7 +12,7 @@ "@hyvor/icons": "^0.0.3" }, "devDependencies": { - "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/adapter-static": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "prettier": "^3.1.1", @@ -981,22 +981,19 @@ "win32" ] }, - "node_modules/@sveltejs/adapter-auto": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.2.5.tgz", - "integrity": "sha512-27LR+uKccZ62lgq4N/hvyU2G+hTP9fxWEAfnZcl70HnyfAjMSsGk1z/SjAPXNCD1mVJIE7IFu3TQ8cQ/UH3c0A==", + "node_modules/@sveltejs/adapter-static": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.5.tgz", + "integrity": "sha512-kFJR7RxeB6FBvrKZWAEzIALatgy11ISaaZbcPup8JdWUdrmmfUHHTJ738YHJTEfnCiiXi6aX8Q6ePY7tnSMD6Q==", "dev": true, - "dependencies": { - "import-meta-resolve": "^4.1.0" - }, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "node_modules/@sveltejs/kit": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.7.1.tgz", - "integrity": "sha512-TBVnkwgYQT3EafGQK6Eyh5FlLEBlRhCmqPTwcdOs+QdnyUc3eCAxRWtXlFxIWtmk6pqv11zdng8qTpThdTogew==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.7.2.tgz", + "integrity": "sha512-bFwrl+0bNr0/DHQZM0INwwSPNYqDjfsKRhUoa6rj9d8tDZzszBrJ3La6/HVFxWGONEigtG+SzHXa1BEa1BLdwA==", "hasInstallScript": true, "dependencies": { "@types/cookie": "^0.6.0", @@ -1260,20 +1257,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/fdir": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", - "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", - "dev": true, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1420,18 +1403,6 @@ "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "peer": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -1492,14 +1463,12 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "optional": true, + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "peer": true, "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -1725,6 +1694,34 @@ "typescript": ">=5.0.0" } }, + "node_modules/svelte-check/node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/svelte-check/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/svelte-hmr": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", diff --git a/website/package.json b/website/package.json index 1c0aebd..241175e 100644 --- a/website/package.json +++ b/website/package.json @@ -12,7 +12,7 @@ "format": "prettier --write ." }, "devDependencies": { - "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/adapter-static": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "prettier": "^3.1.1", diff --git a/website/src/routes/+layout.ts b/website/src/routes/+layout.ts new file mode 100644 index 0000000..c8cacf0 --- /dev/null +++ b/website/src/routes/+layout.ts @@ -0,0 +1 @@ +export const prerender = true; \ No newline at end of file diff --git a/website/svelte.config.js b/website/svelte.config.js index 4a82086..2466eee 100644 --- a/website/svelte.config.js +++ b/website/svelte.config.js @@ -1,4 +1,4 @@ -import adapter from '@sveltejs/adapter-auto'; +import adapter from '@sveltejs/adapter-static'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ From 6146e48f35bcdcf98fe1fb15c98fbf01f35a5506 Mon Sep 17 00:00:00 2001 From: SupunKavinda Date: Tue, 22 Oct 2024 09:20:20 +0530 Subject: [PATCH 2/2] wip --- tests/Feature/UnfoldTest.php | 16 +++------------- ...taPriority.php => MetadataPriorityTest.php} | 8 ++++++++ tests/Unit/RedirectTest.php | 18 ------------------ 3 files changed, 11 insertions(+), 31 deletions(-) rename tests/Unit/Link/Metadata/{MetadataPriority.php => MetadataPriorityTest.php} (80%) delete mode 100644 tests/Unit/RedirectTest.php diff --git a/tests/Feature/UnfoldTest.php b/tests/Feature/UnfoldTest.php index 9d5e1a9..1662609 100644 --- a/tests/Feature/UnfoldTest.php +++ b/tests/Feature/UnfoldTest.php @@ -1,17 +1,7 @@ siteName(); + expect($authors)->toEqual('OG Site name'); }); \ No newline at end of file diff --git a/tests/Unit/RedirectTest.php b/tests/Unit/RedirectTest.php deleted file mode 100644 index 8afb41e..0000000 --- a/tests/Unit/RedirectTest.php +++ /dev/null @@ -1,18 +0,0 @@ -httpClient->sendRequest($request); - dd($response); -})->skip();