Conversation
- Replace full-list /api/persons with search endpoint (?q=, limit=20) to eliminate N+1 lazy loading OOM on the sponsor picker - Refactor SuggestInput to support async mode with debounce (200ms) and replace PersonAutocomplete with SuggestInput<Person> - Add __serialize/__unserialize on User to prevent infinite recursion when Symfony serializes the security token (Person→Sponsor→Person) - Switch Liip Imagine driver from gd to imagick, output webp thumbnails, bust old cache before upload and warm new cache after - Reduce maxWidth/maxHeight from 4096 to 2000 on avatar uploads - Add AvatarCacheService (warmUp + bust) and DeployResetCommand (invalidate sessions + regenerate all avatar caches) - Remove MigratePicturesCommand (migration complete) - Update e2e sponsor-add tests to cover async autocomplete behaviour Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses multiple production-impacting issues around person listing/search, session serialization recursion, and avatar image processing/caching, while updating the sponsor “autocomplete” UX to use an async server-backed suggest input.
Changes:
- Replace “list all persons” behavior with a bounded search endpoint (
/api/persons?q=&limit=) and update the sponsor picker to use async suggestions. - Prevent session serialization recursion by custom-scalar serializing
User; add a deploy-time reset command to clear sessions and regenerate avatar caches. - Switch LiipImagine to Imagick + WebP output and reduce accepted avatar dimensions; add cache bust/warm-up on avatar upload.
Reviewed changes
Copilot reviewed 16 out of 20 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/pages/person/EditPersonPage.tsx | Replaces in-memory person autocomplete with async SuggestInput + server search; removes full-table prefetch. |
| frontend/src/lib/queries.ts | Adds personQueries.search(q, limit) query config for the new search endpoint. |
| frontend/src/lib/api/persons.ts | Replaces getPersons with searchPersons({ q, limit, orderBy }). |
| frontend/src/components/ui/SuggestInput.tsx | Introduces async mode with debounce/loading/empty states while keeping sync mode for existing uses. |
| e2e/tests/profile/sponsor-add.spec.ts | Adds e2e coverage for async autocomplete behavior and updates sponsor-add flow. |
| docker/Dockerfile | Installs Imagick extension and runtime packages for LiipImagine driver switch. |
| backend/src/Service/PersonService.php | Extends getAll() signature to accept search query + limit and forwards to repository. |
| backend/src/Repository/PersonRepository.php | Implements query filtering + max results for person search. |
| backend/src/Controller/Api/PersonApiController.php | Returns [] when q is missing; applies q/limit search; busts + warms avatar caches on upload. |
| backend/src/Service/AvatarCacheService.php | Adds centralized LiipImagine avatar cache bust/warm-up helpers. |
| backend/src/Entity/Person/User.php | Adds __serialize/__unserialize to avoid persisting the full entity graph in session. |
| backend/src/Entity/Person/Person.php | Reduces max allowed uploaded image dimensions (4096 → 2000). |
| backend/src/Command/MigratePicturesCommand.php | Removes the already-completed migration command. |
| backend/src/Command/DeployResetCommand.php | Adds app:deploy-reset to clear sessions and warm avatar caches post-deploy. |
| backend/config/services.yaml | Registers the new deploy reset command with projectDir argument. |
| backend/config/packages/liip_imagine.yaml | Switches driver to imagick and forces avatar filters to output WebP. |
Comments suppressed due to low confidence (1)
e2e/tests/profile/sponsor-add.spec.ts:55
- Avoid fixed
waitForTimeoutin e2e tests; it is inherently flaky across CI load/network variance. Prefer waiting on a deterministic UI condition (e.g., expect a specific option to appear / expect "Aucun résultat" / wait for the/api/persons?q=response).
await autocomplete.fill(firstName);
await page.waitForTimeout(400); // debounce + réseau
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
autoconf is required by phpize/pecl to compile the imagick extension on Alpine Linux. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pecl requires gcc, make, and musl-dev to compile imagick from source. build-base is the Alpine equivalent of Debian's build-essential. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AvatarCacheService: remove leading slash from Liip Imagine path (cache keys must match resolver URLs: uploads/avatars/<file>) - User::__unserialize: use null-coalescing defaults to handle sessions created before this change (prevents 500s on deploy) - DeployResetCommand: guard getRealPath() false return before unlink - AddSponsorForm: wrap searchOtherPersons in useCallback to stabilise the search reference passed to SuggestInput's useEffect dependency - AddSponsorForm: clear otherQuery on successful sponsor submission - AsyncSuggestInput: add role=listbox/option ARIA attributes - e2e: update selectors from listitem/list to option/listbox Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 'Recherche…' intermediate state is too transient to catch reliably in CI (debounce + fast network can complete before Playwright checks). Replace with a direct assertion on results visibility and verify the listbox role is present. Also revert setLoading placement to satisfy react-hooks/set-state-in-effect lint rule. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The API returns last names in uppercase. The test was comparing against mixed-case 'Henri Durand' instead of 'Henri DURAND'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /api/persons without ?q= now returns [] to prevent N+1 OOM. Use loginAs + getMe to retrieve Luka's person ID instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/api/persons: l'endpoint chargeait toute la table avec N+1 lazy loading. Remplacé par un endpoint de recherche?q=&limit=20; retourne[]siqabsent — impossible d'OOM le workerPersistentCollection:Usersérialisé en session descendait dans le graphePerson → Sponsor → Person. Ajout de__serialize/__unserializesurUserpour ne stocker que les scalaires en sessiongdversimagick, output WebP,maxWidth/maxHeightréduit de 4096 à 2000, bust du cache avant upload et warm-up aprèsPersonAutocomplete(synchrone, précharge toute la liste) remplacé parSuggestInput<Person>asynchrone avec debounce 200msDeployResetCommand(app:deploy-reset) : invalide les sessions existantes + régénère tous les caches avatars en WebP — à lancer au déploiementMigratePicturesCommandsupprimée (migration déjà effectuée en prod)Test plan
php bin/console app:deploy-resets'exécute sans erreurGET /api/personssans?q=retourne[](plus de 500)sponsor-add.spec.tspassent🤖 Generated with Claude Code