etape3
This commit is contained in:
@@ -7,8 +7,8 @@
|
||||
| | |
|
||||
|---|---|
|
||||
| **Fait** | Structure dépôt perso, sujet FR, paquet rendu, **projet Flutter** (`swifty_companion/`) |
|
||||
| **Pas commencé** | OAuth Intra, les 2 vues, appels API 42 |
|
||||
| **Prochaine action** | **Étape 2** — [OAuth2 Intra + token unique](etape-02-oauth-intra.md) |
|
||||
| **Fait** | OAuth + token + `/v2/me` (login connecté) |
|
||||
| **Prochaine action** | **Étape 4** — [vue profil complète](etape-04-vue-profil.md) |
|
||||
|
||||
## Arborescence actuelle
|
||||
|
||||
@@ -45,9 +45,9 @@ swifty_companion/ ← dépôt Git de TRAVAIL (perso)
|
||||
|-------|---------|--------|
|
||||
| **0** | Structure, traduction sujet, séparation travail / école, README rendu | ✅ **Terminé** |
|
||||
| **1** | Projet Flutter `swiftyCompanion/swifty_companion/` | ✅ **Terminé** |
|
||||
| **2** | App OAuth sur l’Intra + flux OAuth2 + token unique réutilisé | ⏳ **En cours — prochaine** |
|
||||
| **3** | Vue 1 : recherche login + erreurs (introuvable, réseau) | ⏳ |
|
||||
| **4** | Vue 2 : profil (≥ 4 champs + photo), skills, projets (dont failed) | ⏳ |
|
||||
| **2** | OAuth2 (`IntraAuthService`, `/v2/me`) | ✅ **Validé** (`fgras-ca`) |
|
||||
| **3** | Vue 1 : `SearchScreen` + `IntraUserService` + navigation profil | ✅ **Codé** — à tester sur téléphone |
|
||||
| **4** | Vue 2 : profil complet (≥ 4 champs, skills, projets) | ⏳ **Prochaine** |
|
||||
| **5** | Retour vue 1 + layout adaptatif | ⏳ |
|
||||
| **6** | Passage checklist [swiftyCompanion/README.md](../../swiftyCompanion/README.md) | ⏳ |
|
||||
| **7** | Bonus : refresh token à expiration | ⏳ |
|
||||
|
||||
@@ -19,8 +19,9 @@ Base via la [piscine mobile](../../../piscine_mobile/README.md) sur le même dis
|
||||
|-------|----------|--------|
|
||||
| **0** | Dépôt travail + `docs/`, sujet FR, paquet `swiftyCompanion/` (README / `.gitignore` / `.env.example`) | ✅ **Fait** — détail dans [00-reprise-et-etat.md](00-reprise-et-etat.md) |
|
||||
| **1** | Projet `swiftyCompanion/swifty_companion/` + `flutter run` | ✅ **Fait** |
|
||||
| **2** | OAuth2 Intra : login, stockage token, **un token pour toutes les requêtes** | ⏳ **Prochaine** → [etape-02-oauth-intra.md](etape-02-oauth-intra.md) |
|
||||
| **3** | **Vue 1** : recherche par login + erreurs (404, réseau) | ⏳ → [etape-03-vue-recherche.md](etape-03-vue-recherche.md) |
|
||||
| **2** | OAuth2 Intra : token + `/v2/me` | ✅ Fait |
|
||||
| **3** | **Vue 1** : recherche par login + erreurs | ✅ Codé → tester · [etape-03-vue-recherche.md](etape-03-vue-recherche.md) |
|
||||
| **4** | Vue 2 : profil complet | ⏳ **Prochaine** → [etape-04-vue-profil.md](etape-04-vue-profil.md) |
|
||||
| **4** | **Vue 2** : profil (≥ 4 champs + image), skills, projets (validés + failed) | ⏳ |
|
||||
| **5** | Navigation retour + layout responsive | ⏳ |
|
||||
| **6** | Tests manuels + checklist peer-eval | ⏳ |
|
||||
@@ -38,7 +39,8 @@ README correcteurs (dépôt école) : [swiftyCompanion/README.md](../../swiftyCo
|
||||
|---------|--------|
|
||||
| [00-reprise-et-etat.md](00-reprise-et-etat.md) | ✅ À jour |
|
||||
| [etape-02-oauth-intra.md](etape-02-oauth-intra.md) | ✅ Guide étape 2 |
|
||||
| [etape-03-vue-recherche.md](etape-03-vue-recherche.md) | 🔜 après OAuth |
|
||||
| [etape-03-vue-recherche.md](etape-03-vue-recherche.md) | ✅ Guide étape 3 |
|
||||
| [etape-04-vue-profil.md](etape-04-vue-profil.md) | 🔜 après étape 3 |
|
||||
| `etape-04-vue-profil.md` | 🔜 |
|
||||
| `etape-05-navigation-layout.md` | 🔜 |
|
||||
| `etape-06-bonus-token.md` | 🔜 |
|
||||
|
||||
@@ -1,12 +1,107 @@
|
||||
# Étape 3 — Vue 1 (recherche par login)
|
||||
# Étape 3 — Vue 1 : recherche par login
|
||||
|
||||
> **Prérequis :** [étape 2 — OAuth Intra](etape-02-oauth-intra.md) terminée (token disponible).
|
||||
> **Prérequis :** étape 2 OK — OAuth actif, token unique (`IntraAuthService`).
|
||||
|
||||
## Objectif
|
||||
## Objectif sujet
|
||||
|
||||
- **Première vue** : saisie d’un **login** 42 + bouton rechercher.
|
||||
- Appel API avec le **token OAuth** déjà obtenu.
|
||||
- Gestion des erreurs : **utilisateur introuvable**, **réseau**, token invalide / non connecté.
|
||||
- Navigation vers la **vue 2** si le login existe.
|
||||
| Exigence | Cette étape |
|
||||
|----------|-------------|
|
||||
| Au moins 2 vues | **Vue 1** : recherche · **Vue 2** : profil (étape 4) |
|
||||
| Erreurs gérées | Login introuvable, réseau, non connecté |
|
||||
| Token unique | Réutiliser `IntraAuthService.instance` (ne pas refaire OAuth) |
|
||||
|
||||
Guide détaillé à compléter quand l’étape 2 est OK — demande l’implémentation pas à pas dans le chat.
|
||||
---
|
||||
|
||||
## Parcours utilisateur (vue 1)
|
||||
|
||||
1. L’utilisateur est déjà connecté à l’API (étape 2) — ou bouton « Se connecter » si token absent.
|
||||
2. **Champ texte** + bouton **Rechercher** (login 42, ex. `jdoe`).
|
||||
3. Appel API avec `Authorization: Bearer <token>`.
|
||||
4. **Succès** → navigation vers **vue 2** (profil).
|
||||
5. **Échec** → message clair, pas de crash.
|
||||
|
||||
---
|
||||
|
||||
## API 42 utile
|
||||
|
||||
Doc : [api.intra.42.fr/apidoc](https://api.intra.42.fr/apidoc)
|
||||
|
||||
| Action | Requête typique |
|
||||
|--------|-----------------|
|
||||
| Utilisateur par login | `GET /v2/users/{login}` ou `GET /v2/users?filter[login]=<login>` |
|
||||
| Toi (déjà fait) | `GET /v2/me` |
|
||||
|
||||
Vérifier dans l’apidoc la forme exacte supportée ; beaucoup de projets utilisent :
|
||||
|
||||
```http
|
||||
GET https://api.intra.42.fr/v2/users?filter[login]=jdoe
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
Réponse : tableau JSON ; **vide** = login inconnu.
|
||||
|
||||
---
|
||||
|
||||
## Fichiers à ajouter (étape 3)
|
||||
|
||||
```
|
||||
lib/
|
||||
├── services/
|
||||
│ ├── intra_auth_service.dart # existant
|
||||
│ └── intra_user_service.dart # GET user(s) avec token
|
||||
├── screens/
|
||||
│ ├── auth_screen.dart # option : fusionner ou garder login au démarrage
|
||||
│ ├── search_screen.dart # VUE 1
|
||||
│ └── profile_screen.dart # VUE 2 (squelette → étape 4)
|
||||
└── main.dart # home → SearchScreen si token OK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `IntraUserService` (idée)
|
||||
|
||||
- Méthode `Future<Map<String, dynamic>> fetchUserByLogin(String login)`.
|
||||
- Utilise `IntraAuthService.instance.authHeaders()`.
|
||||
- **404 / liste vide** → exception métier `UserNotFoundException`.
|
||||
- **Socket / timeout** → `NetworkException`.
|
||||
- **401** → token expiré → message « reconnecte-toi ».
|
||||
|
||||
---
|
||||
|
||||
## Gestion des erreurs (sujet)
|
||||
|
||||
| Situation | Message utilisateur (exemple) |
|
||||
|-----------|-------------------------------|
|
||||
| Login inconnu | `Aucun utilisateur pour ce login` |
|
||||
| Pas de réseau | `Connexion impossible. Vérifie le réseau.` |
|
||||
| Non connecté OAuth | `Connecte-toi d’abord à l’API 42` |
|
||||
| Champ vide | `Saisis un login` |
|
||||
|
||||
---
|
||||
|
||||
## Navigation
|
||||
|
||||
```text
|
||||
SearchScreen --[login trouvé]--> ProfileScreen(login)
|
||||
ProfileScreen --[retour]--> SearchScreen
|
||||
```
|
||||
|
||||
`Navigator.push` / `pop` ou `go_router` — rester simple (Navigator 1.0 suffit).
|
||||
|
||||
---
|
||||
|
||||
## Critères « étape 3 terminée »
|
||||
|
||||
| # | Test |
|
||||
|---|------|
|
||||
| 1 | Login valide (ex. `fgras-ca`) → ouvre vue 2 |
|
||||
| 2 | Login `zzzzzzzz` → message erreur |
|
||||
| 3 | Mode avion → message réseau |
|
||||
| 4 | Retour depuis vue 2 → nouvelle recherche possible |
|
||||
| 5 | Un seul OAuth ; les recherches réutilisent le même token |
|
||||
|
||||
---
|
||||
|
||||
## Ensuite — étape 4
|
||||
|
||||
→ [etape-04-vue-profil.md](etape-04-vue-profil.md) : photo, ≥ 4 champs, skills, projets (dont failed), layout adaptatif.
|
||||
|
||||
21
docs/pedagogie/etape-04-vue-profil.md
Normal file
21
docs/pedagogie/etape-04-vue-profil.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Étape 4 — Vue 2 : profil complet
|
||||
|
||||
> **Prérequis :** [étape 3 — recherche](etape-03-vue-recherche.md) (navigation vers vue 2 avec login valide).
|
||||
|
||||
## Objectif sujet (partie obligatoire)
|
||||
|
||||
| Exigence | Détail |
|
||||
|----------|--------|
|
||||
| ≥ 4 infos | login, email, mobile, level, location, wallet, evaluations, … |
|
||||
| Photo | Image de profil (`image_url` / `image.link` selon API) |
|
||||
| Skills | Niveau + pourcentage |
|
||||
| Projets | Terminés **y compris failed** |
|
||||
| Retour | Bouton retour vers vue 1 |
|
||||
| Layout | Responsive (étape 5 peut affiner) |
|
||||
|
||||
## API
|
||||
|
||||
- `GET /v2/users/{id}` avec champs / includes selon apidoc (`cursus_users`, `projects_users`, `skills`, etc.).
|
||||
- Token : toujours via `IntraAuthService`.
|
||||
|
||||
Guide détaillé à rédiger quand l’étape 3 est codée — demande l’implémentation pas à pas dans le chat.
|
||||
@@ -15,22 +15,20 @@
|
||||
|
||||
## URI de redirection
|
||||
|
||||
**Une seule ligne**, sans guillemets :
|
||||
**Téléphone physique (recommandé Swifty)** — une ligne :
|
||||
|
||||
```text
|
||||
com.example.swiftycompanion://callback
|
||||
```
|
||||
|
||||
(Sans `_` dans le schéma — souvent accepté par l’Intra ; sinon voir [oauth-test-manuel.md](oauth-test-manuel.md).)
|
||||
|
||||
**Émulateur seulement** (optionnel) :
|
||||
|
||||
```text
|
||||
http://localhost:8080/callback
|
||||
```
|
||||
|
||||
Si le formulaire affiche encore *invalid format*, tester **une** de ces lignes (toujours une seule) :
|
||||
|
||||
```text
|
||||
http://localhost:3000/callback
|
||||
```
|
||||
|
||||
```text
|
||||
http://127.0.0.1:8080/callback
|
||||
```
|
||||
|
||||
**À ne pas mettre :**
|
||||
|
||||
- `com.example.swifty_companion://callback`
|
||||
|
||||
78
docs/pedagogie/oauth-test-manuel.md
Normal file
78
docs/pedagogie/oauth-test-manuel.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# OAuth 42 — tests manuels des URLs
|
||||
|
||||
## Pourquoi pas `http://localhost` sur téléphone ?
|
||||
|
||||
| Environnement | `http://localhost:8080/callback` |
|
||||
|---------------|----------------------------------|
|
||||
| Émulateur Android | Parfois OK (localhost = l’émulateur) |
|
||||
| **Téléphone physique** | ❌ « Site inaccessible » — localhost = le téléphone, pas ton PC |
|
||||
|
||||
**Swifty Companion (mobile réel)** : utiliser un **schéma d’app** :
|
||||
|
||||
```text
|
||||
com.example.swiftycompanion://callback
|
||||
```
|
||||
|
||||
À enregistrer sur l’Intra **et** dans `.env` (`REDIRECT_URI`).
|
||||
|
||||
---
|
||||
|
||||
## Endpoints 42 (référence sujet)
|
||||
|
||||
| Rôle | URL |
|
||||
|------|-----|
|
||||
| Autorisation | `https://api.intra.42.fr/oauth/authorize` |
|
||||
| Token | `https://api.intra.42.fr/oauth/token` |
|
||||
| API (ex. moi) | `https://api.intra.42.fr/v2/me` |
|
||||
|
||||
Doc : [api.intra.42.fr/apidoc](https://api.intra.42.fr/apidoc)
|
||||
|
||||
---
|
||||
|
||||
## 1. Tester l’API (sans app)
|
||||
|
||||
Dans un navigateur (connecté à l’Intra ou non pour cette étape) :
|
||||
|
||||
```text
|
||||
https://api.intra.42.fr/
|
||||
```
|
||||
|
||||
Réponse JSON attendue (message API) → l’API est joignable.
|
||||
|
||||
---
|
||||
|
||||
## 2. Tester la page d’autorisation (navigateur)
|
||||
|
||||
Remplace `TON_CLIENT_ID` par l’**UID** de ton app OAuth.
|
||||
|
||||
**Redirect actuel (mobile)** :
|
||||
|
||||
```text
|
||||
https://api.intra.42.fr/oauth/authorize?client_id=TON_CLIENT_ID&redirect_uri=com.example.swiftycompanion%3A%2F%2Fcallback&response_type=code&scope=public
|
||||
```
|
||||
|
||||
| Résultat | Signification |
|
||||
|----------|----------------|
|
||||
| Page login / autorisation 42 | ✅ URL d’auth OK |
|
||||
| Erreur `redirect_uri` | ❌ URI pas enregistrée sur l’Intra ou différente de `.env` |
|
||||
| Erreur `invalid_client` | ❌ `client_id` incorrect |
|
||||
|
||||
Après validation, le navigateur tente d’ouvrir `com.example.swiftycompanion://callback?code=...` → sur PC ça échoue (normal) ; **sur le téléphone avec l’app installée**, l’app doit s’ouvrir.
|
||||
|
||||
---
|
||||
|
||||
## 3. Alignement obligatoire (3 endroits identiques)
|
||||
|
||||
1. [Application OAuth Intra](https://profile.intra.42.fr/oauth/applications) → **URI de redirection**
|
||||
2. `swifty_companion/.env` → `REDIRECT_URI=...`
|
||||
3. Code Flutter → même valeur via `IntraConfig.redirectUri`
|
||||
|
||||
---
|
||||
|
||||
## 4. Ancien redirect (à retirer sur l’Intra si présent)
|
||||
|
||||
```text
|
||||
http://localhost:8080/callback
|
||||
```
|
||||
|
||||
Remplacer par `com.example.swiftycompanion://callback` (une ligne dans le formulaire).
|
||||
Reference in New Issue
Block a user