14 KiB
Guide de correction / soutenance — ce qui a été réalisé
Objectif : suivre le sujet Swifty Companion v4 point par point, avec fichiers, comportement et démo à ne pas oublier en peer-eval.
Code évalué : dépôt école = dossierswiftyCompanion/(pasdocs/).
1. Vue d’ensemble
| Élément | Détail |
|---|---|
| Framework | Flutter (Dart), Material 3 |
| API | 42 Intra v2 — https://api.intra.42.fr/v2 |
| Auth | OAuth2 — flutter_appauth 12.x |
| Secrets | .env dans swiftyCompanion/swifty_companion/.env (asset, non versionné) |
| Vues | 2 — recherche (SearchScreen) + profil (ProfileScreen) |
| Bonus | Refresh token sur 401 + retry des requêtes API |
Point d’entrée : swiftyCompanion/swifty_companion/lib/main.dart → home: SearchScreen().
2. Chapitre IV — Instructions générales & sécurité
| Exigence sujet | Réalisation | Fichiers |
|---|---|---|
| Langage mobile (Flutter cité) | Projet Flutter standard | swifty_companion/pubspec.yaml |
| Dernière API 42 | Base URL v2 | lib/config/intra_config.dart — apiBaseUrl |
Secrets en .env, pas dans Git |
CLIENT_ID, CLIENT_SECRET, REDIRECT_URI |
.env (local) · modèle swiftyCompanion/.env.example · ignore swiftyCompanion/.gitignore et .gitignore racine travail |
Chargement .env au démarrage |
dotenv.load |
lib/main.dart + asset dans pubspec.yaml (assets: - .env) |
À dire en correction : les clés OAuth ne sont jamais dans le code source ; l’évaluateur configure son propre .env à partir de .env.example.
3. Chapitre V.1 — Partie obligatoire (9 critères)
Critère 1 — Au moins 2 vues
| Vue | Rôle | Fichier |
|---|---|---|
| Vue 1 | Connexion API + recherche par login | lib/screens/search_screen.dart |
| Vue 2 | Profil étudiant | lib/screens/profile_screen.dart |
Démo : montrer explicitement les deux écrans après une recherche réussie.
Critère 2 — Gérer tous les cas d’erreur
| Situation | Message utilisateur (ex.) | Où c’est géré |
|---|---|---|
| Pas connecté OAuth | « Connecte-toi d’abord à l’API 42 » | intra_api_exception.dart — IntraNotAuthenticatedException · intra_user_service.dart |
| Login vide | « Saisis un login » | IntraUserService.fetchUserByLogin |
| Login inconnu | « Aucun utilisateur pour ce login » | IntraUserNotFoundException |
| Pas de réseau | « Connexion impossible. Vérifie le réseau. » | IntraNetworkException · SocketException / ClientException dans user + profile services |
| OAuth annulé / échec | Messages OAuth / plateforme | search_screen.dart — _connect() |
| Session expirée (401) | « Session expirée. Reconnecte-toi… » | IntraUnauthorizedException · intra_http.dart |
| Accès refusé (403) | Scopes public + projects |
IntraForbiddenException |
| Trop de requêtes (429) | Message + attente / retry | IntraRateLimitException · retry dans intra_http.dart |
| Erreur profil (réseau, API) | Message + bouton Réessayer | profile_screen.dart — _ErrorBody |
.env manquant |
Texte explicite au lancement | search_screen.dart — branche !IntraConfig.isConfigured |
Démo suggérée : login zzzzinvalid → mode avion → (optionnel) profil en erreur + réessai.
Critère 3 — Vue 2 : infos du login s’il existe
- Navigation uniquement si l’API renvoie un utilisateur :
search_screen.dart—_search()→Navigator.push(ProfileScreen(user: …)). - Si liste vide → exception, pas d’ouverture du profil.
Critère 4 — ≥ 4 détails + photo de profil
| Donnée affichée | Source API | Fichier UI |
|---|---|---|
| Login | user['login'] |
AppBar + en-tête profil — profile_screen.dart |
| Photo | image.link ou image_url |
CircleAvatar + NetworkImage — _ProfileHeader |
user['email'] |
_DetailsCard |
|
| Téléphone | user['phone'] |
_DetailsCard |
| Niveau cursus | cursus_users (slug 42) |
IntraProfileService + _DetailsCard |
| Localisation | user['location'] |
_DetailsCard |
| Wallet | user['wallet'] |
_DetailsCard |
Chargement profil : lib/services/intra_profile_service.dart — GET /users/{login}, puis projects_users.
Note sujet : champs masqués par l’API → affichage — (confidentialité OAuth).
Non affiché (optionnel sujet) : evaluation_points — tu as déjà > 4 champs sans lui.
Critère 5 — Skills : niveau + pourcentage
- Pas
/experiences(souvent 403). - Skills depuis
cursus_users[].skillssur la fiche user. - Niveau entier = partie entière du
levelAPI ; % = partie décimale × 100. - Parsing :
IntraProfileService._parseSkillsFromCursus· affichage :_SkillsCard+ barre de progression.
Démo : lire une ligne du type Algorithms — niveau 8 (42 %).
Critère 6 — Projets réalisés, y compris failed
GET /users/{id}/projects_users?page[size]=100- Filtre app : statuts
finishedetfailed—_parseProjectsdansintra_profile_service.dart. - Liste :
_ProjectsCard(libellés Validé / Échec + note si présente).
Scope OAuth requis : projects (sinon 403).
Critère 7 — Revenir à la vue 1
MaterialPageRoute+ retour système / flèche AppBar sur le profil.ProfileScreen:SwiftyUi.appBar(..., showBack: true).- Session OAuth conservée sur
SearchScreen(pas de nouveau login).
Démo : retour → nouvelle recherche sans « Se connecter avec 42 ».
Critère 8 — Layout flexible / moderne
| Mécanisme | Fichier |
|---|---|
| Scroll + clavier | ResponsiveScrollBody — lib/theme/swifty_ui.dart |
| Formulaire centré (vue 1) | searchMaxWidth 480 px |
| Profil 2 colonnes si large | wideBreakpoint 720 px — profile_screen.dart LayoutBuilder |
| Lignes détail empilées si étroit | _DetailRow si largeur < 400 px |
| Thème 42 turquoise | SwiftyUi.brand · main.dart ColorScheme.fromSeed |
Démo : rotation paysage ou grand écran → 2 colonnes sur le profil.
Critère 9 — Un token OAuth, pas un token par requête
| Règle | Implémentation |
|---|---|
| Un seul login OAuth | IntraAuthService.instance — intra_auth_service.dart |
| Bearer sur chaque GET | authHeaders() |
| Pas de OAuth à chaque recherche | IntraUserService / IntraProfileService réutilisent le même service |
À dire : un flux authorizeAndExchangeCode au début ; ensuite uniquement des GET API avec le même access token.
4. OAuth2 Intra (encadrés sujet)
| Élément | Valeur / choix | Fichiers |
|---|---|---|
| Authorize | https://api.intra.42.fr/oauth/authorize |
intra_config.dart |
| Token | https://api.intra.42.fr/oauth/token |
intra_config.dart + refresh dans intra_auth_service.dart |
| Scopes | public, projects |
intra_auth_service.dart — AuthorizationTokenRequest |
| Redirect (device) | com.example.swiftycompanion://callback |
.env · intra-oauth-formulaire.md |
| Android redirect | RedirectUriReceiverActivity |
android/app/src/main/AndroidManifest.xml |
| Gradle scheme | appAuthRedirectScheme |
android/app/build.gradle.kts |
Doc perso (non rendue Intra) : etape-02-oauth-intra.md, oauth-test-manuel.md.
5. Chapitre V.2 — Bonus (refresh token)
| Exigence | Réalisation | Fichiers |
|---|---|---|
| Stocker refresh au login | result.refreshToken |
intra_auth_service.dart — signIn() |
| Rafraîchir à expiration | POST grant_type=refresh_token |
refreshAccessToken() |
| App continue | Sur 401 → refresh → rejouer le GET | intra_http.dart — intraApiGet(..., auth:) |
| Parsing JSON token | intra_oauth_tokens.dart + tests |
|
| Test manuel debug | Bouton « DEBUG : simuler token expiré » | search_screen.dart (kDebugMode uniquement) puis Rechercher |
Limite assumée : tokens en mémoire seulement — après kill de l’app, reconnexion OAuth (acceptable si tu l’expliques).
Guide détaillé : etape-07-bonus-token.md.
6. Inventaire des fichiers applicatifs (lib/)
Fichiers métier à connaître pour la soutenance :
swiftyCompanion/swifty_companion/lib/
├── main.dart # App + thème + dotenv
├── config/
│ └── intra_config.dart # URLs + variables .env
├── services/
│ ├── intra_auth_service.dart # OAuth, access + refresh
│ ├── intra_user_service.dart # Recherche login
│ ├── intra_profile_service.dart # Profil agrégé
│ ├── intra_http.dart # GET + 401/429
│ ├── intra_oauth_tokens.dart # Parse token JSON (bonus)
│ └── intra_api_exception.dart # Erreurs métier
├── screens/
│ ├── search_screen.dart # Vue 1
│ └── profile_screen.dart # Vue 2
└── theme/
└── swifty_ui.dart # Layout partagé (étape 5)
Tests :
test/
├── widget_test.dart # Démarre app + bouton connexion
└── intra_oauth_tokens_test.dart # Parse refresh (bonus)
Fichier supprimé (nettoyage) : auth_screen.dart (écran test obsolète, non utilisé).
Plateforme Android (OAuth physique) : AndroidManifest.xml, build.gradle.kts (voir §4).
7. Endpoints API utilisés
| Usage | Méthode | Appelé depuis |
|---|---|---|
| Session courante | GET /v2/me |
IntraAuthService.fetchMe (après login) |
| Recherche | GET /v2/users?filter[login]= |
IntraUserService |
| Fiche complète | GET /v2/users/{login} |
IntraProfileService |
| Cursus (secours) | GET /v2/users/{id}/cursus_users |
IntraProfileService si pas embarqué |
| Projets | GET /v2/users/{id}/projects_users |
IntraProfileService |
| Refresh token | POST /oauth/token |
IntraAuthService.refreshAccessToken |
Requêtes séquentielles + cache profil 2 min + retry 429 : voir etape-04-vue-profil.md.
8. Chapitre VI — Rendu Git
| À pousser sur Vogosphere | À ne pas pousser |
|---|---|
Contenu de swiftyCompanion/ |
Dossier docs/ (dépôt perso) |
README.md, .gitignore, .env.example |
Fichier .env |
swifty_companion/ (code Flutter) |
.env copy, secrets dans l’historique Git |
Checklist : etape-06-checklist-soutenance.md · swiftyCompanion/README.md.
9. Script de démo (5–8 min)
cd swiftyCompanion/swifty_companion→flutter test(rapide).flutter run -d <device>— montrer.envconfiguré sans afficher les secrets.- Se connecter avec 42 — une fois.
- Recherche login valide → profil : photo, champs, skills, projet failed si dispo.
- Retour → deuxième recherche (même session).
- Login invalide → message.
- Mode avion → erreur réseau.
- Bonus : DEBUG simuler token expiré → Rechercher → OK sans OAuth navigateur.
- Rappeler : 2 vues, 1 token,
.envignoré par git.
10. Parcours pédagogique (docs perso uniquement)
| Étape | Guide |
|---|---|
| 0–1 | 00-reprise-et-etat.md |
| 2 OAuth | etape-02-oauth-intra.md |
| 3 Recherche | etape-03-vue-recherche.md |
| 4 Profil | etape-04-vue-profil.md |
| 5 Layout | etape-05-navigation-layout.md |
| 6 Soutenance | etape-06-checklist-soutenance.md |
| 7 Bonus | etape-07-bonus-token.md |
| Ce document | Cartographie sujet ↔ code pour la correction |
11. Checklist « je n’ai rien oublié » (avant peer-eval)
- App OAuth Intra : scopes public + projects, redirect =
.env .envprésent sur la machine de démo, absent du Git écoleflutter analyze/flutter testOK- Parcours nominal + erreurs + retour testés sur device
- Bonus refresh testé (DEBUG → Rechercher)
- Prêt à expliquer où est chaque critère du sujet (ce guide §3)
- SECRET régénéré si jamais exposé (chat, commit accidentel)
- Fichier
.env copyretiré du Git — voir audit-conformite-usage-api.md
Dernière alignement : sujet PDF v4 — traduction swifty-companion.md.