Files
swift_companion/docs/pedagogie/guide-soutenance-realisation.md
Ladebeze66 a6ab13ee2f fix(profil): photo CDN avec Bearer OAuth et doc soutenance
Charge les avatars depuis cdn.intra.42.fr avec le même access token,
parse image.versions, et documente le flux pour la correction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 10:38:16 +02:00

14 KiB
Raw Blame History

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 = dossier swiftyCompanion/ (pas docs/).


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, image.versions, image_url _imageUrl + _IntraProfileAvatar (http.get + Bearer via cdnImageHeaders) — profile_screen.dart
Email 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[].skills sur la fiche user.
  • Niveau entier = partie entière du level API ; % = 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 finished et failed — _parseProjects dans intra_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)

  1. cd swiftyCompanion/swifty_companion → flutter test (rapide).
  2. flutter run -d <device> — montrer .env configuré sans afficher les secrets.
  3. Se connecter avec 42 — une fois.
  4. Recherche login valide → profil : photo, champs, skills, projet failed si dispo.
  5. Retour → deuxième recherche (même session).
  6. Login invalide → message.
  7. Mode avion → erreur réseau.
  8. Bonus : DEBUG simuler token expiré → Rechercher → OK sans OAuth navigateur.
  9. Rappeler : 2 vues, 1 token, .env ignoré 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
  • .env présent sur la machine de démo, absent du Git école
  • flutter analyze / flutter test OK
  • 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 copy retiré du Git — voir audit-conformite-usage-api.md

Dernière alignement : sujet PDF v4 — traduction swifty-companion.md.