From 02cb5c1d7294cdc51359e4caed7c170388166ecd Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Fri, 15 Dec 2023 20:55:24 +0000 Subject: [PATCH 01/11] feat: Add Google Photo Suggestion Service --- .../ManagePhotos/Services/GooglePhoto.php | 106 ++++++++++++++++++ .../Services/GooglePhotoTest.php | 52 +++++++++ 2 files changed, 158 insertions(+) create mode 100644 app/Domains/Contact/ManagePhotos/Services/GooglePhoto.php create mode 100644 tests/Unit/Domains/Contact/ManageContact/Services/GooglePhotoTest.php diff --git a/app/Domains/Contact/ManagePhotos/Services/GooglePhoto.php b/app/Domains/Contact/ManagePhotos/Services/GooglePhoto.php new file mode 100644 index 00000000000..2ae6f6cff46 --- /dev/null +++ b/app/Domains/Contact/ManagePhotos/Services/GooglePhoto.php @@ -0,0 +1,106 @@ + 'isch', + 'tbs' => 'iar:xw,ift:png', + ]; + + /** + * Get the permissions that apply to the user calling the service. + */ + public function permissions(): array + { + return [ + 'author_must_belong_to_account', + 'vault_must_belong_to_account', + 'author_must_be_vault_editor', + 'contact_must_belong_to_vault', + ]; + } + + /** + * Create a pet. + * + * @throws Exception + */ + public function execute(string $searchTerm): array + { + $html = $this->search($searchTerm); + + return $this->imageUrls($html); + } + + /** + * Set the params for the service. + */ + public function params(array $params): self + { + $this->params = $params; + + return $this; + } + + /** + * Extract image URLs from the HTML. + */ + private function imageUrls(string $html): array + { + $imageUrls = []; + + if (empty($html)) { + return $imageUrls; + } + + $doc = new DOMDocument(); + @$doc->loadHTML($html); + + $imgTags = $doc->getElementsByTagName('img'); + + foreach ($imgTags as $imgTag) { + $src = $imgTag->getAttribute('src'); + if (filter_var($src, FILTER_VALIDATE_URL)) { + $imageUrls[] = $src; + } + } + + return $imageUrls; + } + + /** + * Fetch the HTML from Google. + * + * @throws Exception + */ + private function search(string $searchTerm): string + { + $params = array_merge($this->params, [ + 'q' => $searchTerm, + ]); + + try { + $response = Http::get(self::GOOGLE_SEARCH_URL, $params); + } catch (Exception $e) { + throw new Exception('Failed to fetch data from Google.'); + } + + return $response->body() ?? ''; + } +} diff --git a/tests/Unit/Domains/Contact/ManageContact/Services/GooglePhotoTest.php b/tests/Unit/Domains/Contact/ManageContact/Services/GooglePhotoTest.php new file mode 100644 index 00000000000..73f3aaa6891 --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageContact/Services/GooglePhotoTest.php @@ -0,0 +1,52 @@ + Http::response('', 200), + ]); + + $service = new GooglePhoto(); + + $imageUrls = $service->execute('Laravel'); + } + + /** + * @test + * + * @group google-fetch + */ + public function it_fetches_image_urls(): void + { + + } + + /** + * @test + * + * @group google-parse + */ + public function it_parses_html(): void + { + + } +} From 2ecdbd0d5457d781a2526209eefaf6ab25f90736 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Tue, 26 Dec 2023 12:26:47 +0000 Subject: [PATCH 02/11] add photo suggestion input --- resources/js/Shared/Form/PhotoSuggestion.vue | 25 ++++++ resources/js/Shared/Modules/ContactAvatar.vue | 77 ++++++++++++++++++- resources/js/Shared/Modules/ContactName.vue | 4 + 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 resources/js/Shared/Form/PhotoSuggestion.vue diff --git a/resources/js/Shared/Form/PhotoSuggestion.vue b/resources/js/Shared/Form/PhotoSuggestion.vue new file mode 100644 index 00000000000..46f189f9bdc --- /dev/null +++ b/resources/js/Shared/Form/PhotoSuggestion.vue @@ -0,0 +1,25 @@ + + + diff --git a/resources/js/Shared/Modules/ContactAvatar.vue b/resources/js/Shared/Modules/ContactAvatar.vue index 7976642355a..931211b4468 100644 --- a/resources/js/Shared/Modules/ContactAvatar.vue +++ b/resources/js/Shared/Modules/ContactAvatar.vue @@ -1,15 +1,54 @@ diff --git a/resources/js/Shared/Modules/ContactName.vue b/resources/js/Shared/Modules/ContactName.vue index 45aef2b58fd..46aca21e06c 100644 --- a/resources/js/Shared/Modules/ContactName.vue +++ b/resources/js/Shared/Modules/ContactName.vue @@ -73,6 +73,7 @@ export default { data: { type: Object, default: null, + isOpen: false, }, }, @@ -98,6 +99,9 @@ export default { this.form.errors = error.response.data; }); }, + toggleOpen() { + this.isOpen = !this.isOpen; + }, }, }; From c5c0570eb3bc3e8f510b22402c2dcd57843f1597 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Tue, 26 Dec 2023 15:28:37 +0000 Subject: [PATCH 03/11] add avatar suggest module --- .../ManageAvatar/Services/SuggestAvatar.php | 69 +++++++++++++++++++ .../Controllers/ModuleAvatarController.php | 17 +++++ .../GooglePhotoService.php} | 39 ++--------- routes/web.php | 1 + ...glePhotoTest.php => SuggestAvatarTest.php} | 10 +-- 5 files changed, 94 insertions(+), 42 deletions(-) create mode 100644 app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php rename app/{Domains/Contact/ManagePhotos/Services/GooglePhoto.php => Services/GooglePhotoService.php} (60%) rename tests/Unit/Domains/Contact/ManageContact/Services/{GooglePhotoTest.php => SuggestAvatarTest.php} (64%) diff --git a/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php b/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php new file mode 100644 index 00000000000..60f71422a78 --- /dev/null +++ b/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php @@ -0,0 +1,69 @@ + 'required|uuid|exists:accounts,id', + 'vault_id' => 'required|uuid|exists:vaults,id', + 'author_id' => 'required|uuid|exists:users,id', + 'contact_id' => 'required|uuid|exists:contacts,id', + 'search_term' => 'nullable|string', + ]; + } + + /** + * Get the permissions that apply to the user calling the service. + */ + public function permissions(): array + { + return [ + 'author_must_belong_to_account', + 'vault_must_belong_to_account', + 'author_must_be_vault_editor', + 'contact_must_belong_to_vault', + ]; + } + + /** + * Remove the current file used as avatar and put the default avatar back. + */ + public function execute(array $data): array + { + $this->data = $data; + $this->validate(); + + $search_term = $data['search_term'] ?? $this->contact->name; + + if (empty($search_term)) { + return []; + } + + try { + return (new GooglePhotoService())->search($search_term); + } catch (\Exception $e) { + // TODO: log error + return []; + } + } + + /** + * @throws \Exception + */ + private function validate(): void + { + $this->validateRules($this->data); + } +} diff --git a/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php b/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php index 12b06847bbd..379da401e89 100644 --- a/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php +++ b/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php @@ -3,10 +3,12 @@ namespace App\Domains\Contact\ManageAvatar\Web\Controllers; use App\Domains\Contact\ManageAvatar\Services\DestroyAvatar; +use App\Domains\Contact\ManageAvatar\Services\SuggestAvatar; use App\Domains\Contact\ManageAvatar\Services\UpdatePhotoAsAvatar; use App\Domains\Contact\ManageDocuments\Services\UploadFile; use App\Http\Controllers\Controller; use App\Models\File; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; @@ -66,4 +68,19 @@ public function destroy(Request $request, string $vaultId, string $contactId) ]), ], 200); } + + public function suggest(Request $request, string $vaultId, string $contactId): JsonResponse + { + $data = [ + 'account_id' => Auth::user()->account_id, + 'author_id' => Auth::id(), + 'vault_id' => $vaultId, + 'contact_id' => $contactId, + 'search_term' => $request->input('search_term'), + ]; + + $imageUrls = (new SuggestAvatar())->execute($data); + + return response()->json($imageUrls, 200); + } } diff --git a/app/Domains/Contact/ManagePhotos/Services/GooglePhoto.php b/app/Services/GooglePhotoService.php similarity index 60% rename from app/Domains/Contact/ManagePhotos/Services/GooglePhoto.php rename to app/Services/GooglePhotoService.php index 2ae6f6cff46..7dae15777fe 100644 --- a/app/Domains/Contact/ManagePhotos/Services/GooglePhoto.php +++ b/app/Services/GooglePhotoService.php @@ -1,14 +1,12 @@ 'iar:xw,ift:png', ]; - /** - * Get the permissions that apply to the user calling the service. - */ - public function permissions(): array - { - return [ - 'author_must_belong_to_account', - 'vault_must_belong_to_account', - 'author_must_be_vault_editor', - 'contact_must_belong_to_vault', - ]; - } - - /** - * Create a pet. - * - * @throws Exception - */ - public function execute(string $searchTerm): array - { - $html = $this->search($searchTerm); - - return $this->imageUrls($html); - } - /** * Set the params for the service. */ @@ -61,7 +34,7 @@ public function params(array $params): self /** * Extract image URLs from the HTML. */ - private function imageUrls(string $html): array + public function imageUrls(string $html): array { $imageUrls = []; @@ -89,18 +62,18 @@ private function imageUrls(string $html): array * * @throws Exception */ - private function search(string $searchTerm): string + public function search(string $searchTerm): array { $params = array_merge($this->params, [ 'q' => $searchTerm, ]); try { - $response = Http::get(self::GOOGLE_SEARCH_URL, $params); + $html = Http::get(self::GOOGLE_SEARCH_URL, $params)->body(); } catch (Exception $e) { throw new Exception('Failed to fetch data from Google.'); } - return $response->body() ?? ''; + return $this->imageUrls($html); } } diff --git a/routes/web.php b/routes/web.php index a3337c2ec73..3175fbf7ba9 100644 --- a/routes/web.php +++ b/routes/web.php @@ -278,6 +278,7 @@ // avatar Route::put('avatar', [ModuleAvatarController::class, 'update'])->name('contact.avatar.update'); Route::delete('avatar', [ModuleAvatarController::class, 'destroy'])->name('contact.avatar.destroy'); + Route::get('avatar/suggest', [ModuleAvatarController::class, 'suggest'])->name('contact.avatar.suggest'); // contact feed entries Route::get('feed', [ContactFeedController::class, 'show'])->name('contact.feed.show'); diff --git a/tests/Unit/Domains/Contact/ManageContact/Services/GooglePhotoTest.php b/tests/Unit/Domains/Contact/ManageContact/Services/SuggestAvatarTest.php similarity index 64% rename from tests/Unit/Domains/Contact/ManageContact/Services/GooglePhotoTest.php rename to tests/Unit/Domains/Contact/ManageContact/Services/SuggestAvatarTest.php index 73f3aaa6891..aaaad7aff82 100644 --- a/tests/Unit/Domains/Contact/ManageContact/Services/GooglePhotoTest.php +++ b/tests/Unit/Domains/Contact/ManageContact/Services/SuggestAvatarTest.php @@ -2,13 +2,11 @@ namespace Tests\Unit\Domains\Contact\ManageContact\Services; -use App\Domains\Contact\ManagePhotos\Services\GooglePhoto; use Exception; use Illuminate\Foundation\Testing\DatabaseTransactions; -use Illuminate\Support\Facades\Http; use Tests\TestCase; -class GooglePhotoTest extends TestCase +class SuggestAvatarTest extends TestCase { use DatabaseTransactions; @@ -21,13 +19,7 @@ class GooglePhotoTest extends TestCase */ public function it_searches_google(): void { - Http::fake([ - GooglePhoto::GOOGLE_SEARCH_URL.'/*' => Http::response('', 200), - ]); - $service = new GooglePhoto(); - - $imageUrls = $service->execute('Laravel'); } /** From 4e1f7bd19278778e22976500dad89211a142dc92 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Fri, 29 Dec 2023 20:29:38 +0000 Subject: [PATCH 04/11] add avatar suggestion in vue --- .../Controllers/ModuleAvatarController.php | 4 +- .../ViewHelpers/ModuleAvatarViewHelper.php | 16 +++ app/Services/GooglePhotoService.php | 1 - config/monica.php | 2 + resources/js/Shared/Form/AvatarSuggestion.vue | 28 +++++ resources/js/Shared/Form/PhotoSuggestion.vue | 25 ----- resources/js/Shared/Modules/ContactAvatar.vue | 102 ++++++++++++------ resources/views/app.blade.php | 4 +- 8 files changed, 124 insertions(+), 58 deletions(-) create mode 100644 resources/js/Shared/Form/AvatarSuggestion.vue delete mode 100644 resources/js/Shared/Form/PhotoSuggestion.vue diff --git a/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php b/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php index 379da401e89..deed7f3c226 100644 --- a/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php +++ b/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php @@ -81,6 +81,8 @@ public function suggest(Request $request, string $vaultId, string $contactId): J $imageUrls = (new SuggestAvatar())->execute($data); - return response()->json($imageUrls, 200); + return response()->json([ + 'data' => $imageUrls, + ]); } } diff --git a/app/Domains/Contact/ManageAvatar/Web/ViewHelpers/ModuleAvatarViewHelper.php b/app/Domains/Contact/ManageAvatar/Web/ViewHelpers/ModuleAvatarViewHelper.php index aba2e0cf332..8eba6feef40 100644 --- a/app/Domains/Contact/ManageAvatar/Web/ViewHelpers/ModuleAvatarViewHelper.php +++ b/app/Domains/Contact/ManageAvatar/Web/ViewHelpers/ModuleAvatarViewHelper.php @@ -2,6 +2,7 @@ namespace App\Domains\Contact\ManageAvatar\Web\ViewHelpers; +use App\Helpers\StorageHelper; use App\Models\Contact; class ModuleAvatarViewHelper @@ -10,6 +11,21 @@ public static function data(Contact $contact): array { return [ 'avatar' => $contact->avatar, + 'uploadcare' => StorageHelper::uploadcare(), + 'url' => [ + 'update' => route('contact.avatar.update', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + 'destroy' => route('contact.avatar.destroy', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + 'suggest' => route('contact.avatar.suggest', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + ], ]; } } diff --git a/app/Services/GooglePhotoService.php b/app/Services/GooglePhotoService.php index 7dae15777fe..652f5ab59af 100644 --- a/app/Services/GooglePhotoService.php +++ b/app/Services/GooglePhotoService.php @@ -18,7 +18,6 @@ class GooglePhotoService */ private array $params = [ 'tbm' => 'isch', - 'tbs' => 'iar:xw,ift:png', ]; /** diff --git a/config/monica.php b/config/monica.php index fed12ab04e2..1f4357f04d0 100644 --- a/config/monica.php +++ b/config/monica.php @@ -144,4 +144,6 @@ 'settings_preferences_maps' => 'user-and-account-settings/manage-preferences#timezone', 'settings_account_deletion' => 'user-and-account-settings/account-deletion', ], + + 'uploadcare_public_key' => env('UPLOADCARE_PUBLIC_KEY', null), ]; diff --git a/resources/js/Shared/Form/AvatarSuggestion.vue b/resources/js/Shared/Form/AvatarSuggestion.vue new file mode 100644 index 00000000000..b1cfebe66ee --- /dev/null +++ b/resources/js/Shared/Form/AvatarSuggestion.vue @@ -0,0 +1,28 @@ + + + diff --git a/resources/js/Shared/Form/PhotoSuggestion.vue b/resources/js/Shared/Form/PhotoSuggestion.vue deleted file mode 100644 index 46f189f9bdc..00000000000 --- a/resources/js/Shared/Form/PhotoSuggestion.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - diff --git a/resources/js/Shared/Modules/ContactAvatar.vue b/resources/js/Shared/Modules/ContactAvatar.vue index 931211b4468..1e058987b48 100644 --- a/resources/js/Shared/Modules/ContactAvatar.vue +++ b/resources/js/Shared/Modules/ContactAvatar.vue @@ -1,6 +1,6 @@ - + @routes @vite('resources/js/app.js') @inertiaHead From 2de46c20721078e1135915ec0194ea9f7e0b3368 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Fri, 29 Dec 2023 21:05:55 +0000 Subject: [PATCH 05/11] remove console log --- resources/js/Shared/Modules/ContactAvatar.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/js/Shared/Modules/ContactAvatar.vue b/resources/js/Shared/Modules/ContactAvatar.vue index 1e058987b48..7392af0cecd 100644 --- a/resources/js/Shared/Modules/ContactAvatar.vue +++ b/resources/js/Shared/Modules/ContactAvatar.vue @@ -126,7 +126,6 @@ export default { axios .put(this.data.url.update, form) .then((response) => { - console.log(response, 555); router.visit(response.data.data); flash(trans('Avatar has been added'), 'success'); }) From 4b62171aa6481a33efb150a31a8d9599882974b2 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Fri, 29 Dec 2023 21:06:32 +0000 Subject: [PATCH 06/11] add uploadcare public key --- resources/views/app.blade.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/views/app.blade.php b/resources/views/app.blade.php index 289cb7cf5a5..8b3db2025bd 100644 --- a/resources/views/app.blade.php +++ b/resources/views/app.blade.php @@ -30,9 +30,8 @@ } else { document.documentElement.classList.remove('dark') } - - @routes @vite('resources/js/app.js') From 4cb6f4516708ab050c5fb82fc52fe9b481b2d050 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Sat, 30 Dec 2023 10:31:32 +0000 Subject: [PATCH 07/11] clean up code style --- resources/js/Shared/Modules/ContactAvatar.vue | 2 +- resources/js/Shared/Modules/ContactName.vue | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/resources/js/Shared/Modules/ContactAvatar.vue b/resources/js/Shared/Modules/ContactAvatar.vue index 7392af0cecd..be984b92369 100644 --- a/resources/js/Shared/Modules/ContactAvatar.vue +++ b/resources/js/Shared/Modules/ContactAvatar.vue @@ -36,8 +36,8 @@ import Avatar from '@/Shared/Avatar.vue'; import PhotoSuggestion from '@/Shared/Form/AvatarSuggestion.vue'; import { flash } from '@/methods'; import { trans } from 'laravel-vue-i18n'; -import uploadcare from 'uploadcare-widget'; import { router } from '@inertiajs/vue3'; +import uploadcare from 'uploadcare-widget'; export default { components: { diff --git a/resources/js/Shared/Modules/ContactName.vue b/resources/js/Shared/Modules/ContactName.vue index 46aca21e06c..45aef2b58fd 100644 --- a/resources/js/Shared/Modules/ContactName.vue +++ b/resources/js/Shared/Modules/ContactName.vue @@ -73,7 +73,6 @@ export default { data: { type: Object, default: null, - isOpen: false, }, }, @@ -99,9 +98,6 @@ export default { this.form.errors = error.response.data; }); }, - toggleOpen() { - this.isOpen = !this.isOpen; - }, }, }; From beca44089e390e83d41429f506c4d4c9659414b7 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Sat, 30 Dec 2023 10:33:22 +0000 Subject: [PATCH 08/11] clean up code style --- app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php b/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php index 60f71422a78..e2ac154375b 100644 --- a/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php +++ b/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php @@ -54,7 +54,6 @@ public function execute(array $data): array try { return (new GooglePhotoService())->search($search_term); } catch (\Exception $e) { - // TODO: log error return []; } } From 4f47dc7f910faaf489508d515f5b6863551b02a6 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Sat, 30 Dec 2023 10:35:08 +0000 Subject: [PATCH 09/11] remove console log --- resources/js/Shared/Form/AvatarSuggestion.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/js/Shared/Form/AvatarSuggestion.vue b/resources/js/Shared/Form/AvatarSuggestion.vue index b1cfebe66ee..68618f6c90a 100644 --- a/resources/js/Shared/Form/AvatarSuggestion.vue +++ b/resources/js/Shared/Form/AvatarSuggestion.vue @@ -10,7 +10,6 @@ const select = (selectedIndex) => { props.photos.map(function (photo, index) { photo.selected = selectedIndex === index; if (photo.selected) { - console.log(photo); emit('update:modelValue', photo); emit('select', photo); } From 5f318074314e167bf2a0e10e9dae7a7c7c350e57 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Sat, 30 Dec 2023 10:38:03 +0000 Subject: [PATCH 10/11] remove unused lines --- resources/js/Shared/Modules/ContactAvatar.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/js/Shared/Modules/ContactAvatar.vue b/resources/js/Shared/Modules/ContactAvatar.vue index be984b92369..2a316ba9c15 100644 --- a/resources/js/Shared/Modules/ContactAvatar.vue +++ b/resources/js/Shared/Modules/ContactAvatar.vue @@ -27,7 +27,6 @@ - From 9060a4459b20782ad97b2c8cbea4417a6c5aa355 Mon Sep 17 00:00:00 2001 From: Emeka Mbah Date: Sun, 31 Dec 2023 17:35:06 +0000 Subject: [PATCH 11/11] add test for suggest avatar --- .../ManageAvatar/Services/SuggestAvatar.php | 42 +++++-- .../Controllers/ModuleAvatarController.php | 10 +- app/Services/GooglePhotoService.php | 11 +- .../Services/SuggestAvatarTest.php | 107 ++++++++++++++++++ .../Services/SuggestAvatarTest.php | 44 ------- 5 files changed, 155 insertions(+), 59 deletions(-) create mode 100644 tests/Unit/Domains/Contact/ManageAvatar/Services/SuggestAvatarTest.php delete mode 100644 tests/Unit/Domains/Contact/ManageContact/Services/SuggestAvatarTest.php diff --git a/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php b/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php index e2ac154375b..af28740856e 100644 --- a/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php +++ b/app/Domains/Contact/ManageAvatar/Services/SuggestAvatar.php @@ -5,13 +5,19 @@ use App\Interfaces\ServiceInterface; use App\Services\BaseService; use App\Services\GooglePhotoService; +use Exception; class SuggestAvatar extends BaseService implements ServiceInterface { - private array $data; + /** + * The contact instance. + */ + private string $search_term; /** * Get the validation rules that apply to the service. + * + * @return array */ public function rules(): array { @@ -26,6 +32,8 @@ public function rules(): array /** * Get the permissions that apply to the user calling the service. + * + * @return array */ public function permissions(): array { @@ -39,30 +47,42 @@ public function permissions(): array /** * Remove the current file used as avatar and put the default avatar back. + * + * @throws Exception */ public function execute(array $data): array { - $this->data = $data; - $this->validate(); - - $search_term = $data['search_term'] ?? $this->contact->name; + $this->validate($data); + $this->setSearchTerm($data); - if (empty($search_term)) { + if (empty($this->getSearchTerm())) { return []; } try { - return (new GooglePhotoService())->search($search_term); - } catch (\Exception $e) { + return (new GooglePhotoService())->search($this->getSearchTerm()); + } catch (Exception $e) { return []; } } + public function getSearchTerm(): string + { + return $this->search_term; + } + + public function setSearchTerm(array $data): self + { + $this->search_term = $data['search_term'] ?? $this->contact->name; + + return $this; + } + /** - * @throws \Exception + * @throws Exception */ - private function validate(): void + private function validate(array $data): void { - $this->validateRules($this->data); + $this->validateRules($data); } } diff --git a/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php b/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php index deed7f3c226..c2023c20ece 100644 --- a/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php +++ b/app/Domains/Contact/ManageAvatar/Web/Controllers/ModuleAvatarController.php @@ -71,12 +71,16 @@ public function destroy(Request $request, string $vaultId, string $contactId) public function suggest(Request $request, string $vaultId, string $contactId): JsonResponse { + $accountId = Auth::user()->account_id; + $authorId = Auth::id(); + $searchTerm = $request->search_term; + $data = [ - 'account_id' => Auth::user()->account_id, - 'author_id' => Auth::id(), + 'account_id' => $accountId, + 'author_id' => $authorId, 'vault_id' => $vaultId, 'contact_id' => $contactId, - 'search_term' => $request->input('search_term'), + 'search_term' => $searchTerm, ]; $imageUrls = (new SuggestAvatar())->execute($data); diff --git a/app/Services/GooglePhotoService.php b/app/Services/GooglePhotoService.php index 652f5ab59af..134c4434501 100644 --- a/app/Services/GooglePhotoService.php +++ b/app/Services/GooglePhotoService.php @@ -5,6 +5,7 @@ use DOMDocument; use Exception; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Str; class GooglePhotoService { @@ -13,6 +14,11 @@ class GooglePhotoService */ public const GOOGLE_SEARCH_URL = 'https://www.google.com/search'; + /** + * Google image URL. + */ + public const GOOGLE_IMAGE_URL = 'https://encrypted-tbn0.gstatic.com'; + /** * The params for Google search. */ @@ -48,7 +54,10 @@ public function imageUrls(string $html): array foreach ($imgTags as $imgTag) { $src = $imgTag->getAttribute('src'); - if (filter_var($src, FILTER_VALIDATE_URL)) { + + // Add only full-size images (exclude thumbnails, etc.) + if (filter_var($src, FILTER_VALIDATE_URL) + && Str::startsWith($src, self::GOOGLE_IMAGE_URL)) { $imageUrls[] = $src; } } diff --git a/tests/Unit/Domains/Contact/ManageAvatar/Services/SuggestAvatarTest.php b/tests/Unit/Domains/Contact/ManageAvatar/Services/SuggestAvatarTest.php new file mode 100644 index 00000000000..7d98349b147 --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageAvatar/Services/SuggestAvatarTest.php @@ -0,0 +1,107 @@ +fakeHttpResponse(); + $request = $this->requestParams(); + + $suggestAvatar = new SuggestAvatar(); + $suggestions = $suggestAvatar->execute($request); + + $this->assertCount(2, $suggestions); + $this->assertEquals( + $suggestions, + array_slice($this->suggestions, 0, 2) + ); + + $contact = Contact::find($request['contact_id']); + + $this->assertSame($suggestAvatar->getSearchTerm(), $contact->name); + } + + /** + * @test + * + * @group suggest-avatar + * @group it_suggests_list_based_on_search_term + * + * @throws Exception + */ + public function it_suggests_list_based_on_search_term(): void + { + $this->fakeHttpResponse(); + $request = $this->requestParams(); + $request['search_term'] = 'John Doe'; + + $suggestAvatar = new SuggestAvatar(); + $suggestions = $suggestAvatar->execute($request); + + $this->assertCount(2, $suggestions); + $this->assertEquals( + $suggestions, + array_slice($this->suggestions, 0, 2) + ); + + $this->assertSame($suggestAvatar->getSearchTerm(), $request['search_term']); + } + + private function requestParams(): array + { + $author = $this->createUser(); + $vault = $this->createVault($author->account); + $vault = $this->setPermissionInVault($author, Vault::PERMISSION_EDIT, $vault); + $contact = Contact::factory()->create(['vault_id' => $vault->id]); + + return [ + 'account_id' => $author->account->id, + 'vault_id' => $vault->id, + 'author_id' => $author->id, + 'contact_id' => $contact->id, + ]; + } + + /** + * @throws Exception + */ + private function fakeHttpResponse(): void + { + Http::fake(function () { + return Http::response(' + + + + + + + '); + }); + } +} diff --git a/tests/Unit/Domains/Contact/ManageContact/Services/SuggestAvatarTest.php b/tests/Unit/Domains/Contact/ManageContact/Services/SuggestAvatarTest.php deleted file mode 100644 index aaaad7aff82..00000000000 --- a/tests/Unit/Domains/Contact/ManageContact/Services/SuggestAvatarTest.php +++ /dev/null @@ -1,44 +0,0 @@ -