diff --git a/MISSION_INTEGRATION_IDS_ISSUE_FIX.md b/MISSION_INTEGRATION_IDS_ISSUE_FIX.md new file mode 100644 index 00000000..4233dc4e --- /dev/null +++ b/MISSION_INTEGRATION_IDS_ISSUE_FIX.md @@ -0,0 +1,253 @@ +# Fix: IDs d'IntĂ©gration Vides lors de la Suppression + +## 🔍 ProblĂšme IdentifiĂ© + +Lors de la suppression d'une mission, N8N reçoit des IDs vides : + +```json +{ + "missionId": "cd0225cf-8dfd-4bf0-a20a-6aa9c04ebb42", + "name": "Creation", + "repoName": "", + "leantimeProjectId": 0, + "documentationCollectionId": "", + "rocketchatChannelId": "", + "giteaRepositoryUrl": null, + "outlineCollectionId": null, + "rocketChatChannelId": null +} +``` + +**Cause** : Les IDs retournĂ©s par N8N lors de la crĂ©ation ne sont **pas sauvegardĂ©s en base**. + +--- + +## 🔍 Analyse du ProblĂšme + +### Flow Actuel + +``` +1. POST /api/missions → CrĂ©e mission en Prisma +2. Upload logo dans Minio +3. POST N8N webhook → N8N crĂ©e intĂ©grations +4. N8N → POST /mission-created (avec IDs) +5. ❌ Endpoint cherche mission par name + creatorId (peut Ă©chouer) +6. ❌ IDs jamais sauvegardĂ©s +7. ❌ Lors de suppression → IDs vides +``` + +### ProblĂšmes IdentifiĂ©s + +1. **Recherche de mission fragile** : L'endpoint `/mission-created` cherche par `name` + `creatorId`, ce qui peut Ă©chouer si : + - Plusieurs missions ont le mĂȘme nom + - Le nom a changĂ© + - Le creatorId ne correspond pas exactement + +2. **missionId non envoyĂ©** : On n'envoie pas le `missionId` Ă  N8N, donc N8N ne peut pas le renvoyer + +3. **N8N ne renvoie peut-ĂȘtre pas missionId** : MĂȘme si on l'envoie, N8N doit le renvoyer dans `/mission-created` + +--- + +## ✅ Solutions ImplĂ©mentĂ©es + +### 1. Envoyer missionId Ă  N8N + +**Fichier** : `app/api/missions/route.ts` + +```typescript +const n8nData = { + ...body, + missionId: mission.id, // ✅ Send missionId so N8N can return it + creatorId: userId, + logoPath: logoPath, + logoUrl: logoUrl, + config: { ... } +}; +``` + +**Avantage** : N8N peut maintenant renvoyer le `missionId` dans `/mission-created` + +### 2. AmĂ©liorer la Recherche de Mission + +**Fichier** : `app/api/missions/mission-created/route.ts` + +```typescript +// Prefer missionId if provided, otherwise use name + creatorId +let mission; + +if (body.missionId) { + // ✅ Use missionId if provided (more reliable) + mission = await prisma.mission.findUnique({ + where: { id: body.missionId } + }); +} else if (body.name && body.creatorId) { + // Fallback to name + creatorId (for backward compatibility) + mission = await prisma.mission.findFirst({ + where: { + name: body.name, + creatorId: body.creatorId + }, + orderBy: { createdAt: 'desc' } + }); +} +``` + +**Avantages** : +- ✅ Recherche par `missionId` (plus fiable) +- ✅ Fallback vers `name` + `creatorId` (rĂ©trocompatibilitĂ©) +- ✅ Gestion d'erreurs amĂ©liorĂ©e + +--- + +## 📋 Format de RequĂȘte N8N → /mission-created + +### Format RecommandĂ© (avec missionId) + +```json +{ + "missionId": "cd0225cf-8dfd-4bf0-a20a-6aa9c04ebb42", + "name": "Creation", + "creatorId": "user-id", + "gitRepoUrl": "https://gite.slm-lab.net/alma/creation", + "leantimeProjectId": "123", + "documentationCollectionId": "collection-456", + "rocketchatChannelId": "channel-789" +} +``` + +### Format de Fallback (sans missionId) + +```json +{ + "name": "Creation", + "creatorId": "user-id", + "gitRepoUrl": "https://gite.slm-lab.net/alma/creation", + "leantimeProjectId": "123", + "documentationCollectionId": "collection-456", + "rocketchatChannelId": "channel-789" +} +``` + +--- + +## 🔧 Action Requise dans N8N + +### Modifier le Node "Save Mission To API" + +Le node N8N doit inclure `missionId` dans le body : + +**Avant** : +```json +{ + "name": "{{ name }}", + "creatorId": "{{ creatorId }}", + "gitRepoUrl": "{{ gitRepo.html_url }}", + ... +} +``` + +**AprĂšs** : +```json +{ + "missionId": "{{ missionId }}", // ✅ Ajouter missionId + "name": "{{ name }}", + "creatorId": "{{ creatorId }}", + "gitRepoUrl": "{{ gitRepo.html_url }}", + "leantimeProjectId": "{{ leantimeProject.result[0] }}", + "documentationCollectionId": "{{ docCollection.data.id }}", + "rocketchatChannelId": "{{ rocketChatChannel.channel._id }}", + ... +} +``` + +**OĂč trouver missionId dans N8N** : +- Il est dans les donnĂ©es initiales : `{{ $node['Process Mission Data'].json.missionId }}` +- Ou dans le body original : `{{ $json.missionId }}` + +--- + +## đŸ§Ș Tests + +### Test 1: VĂ©rifier missionId est envoyĂ© Ă  N8N + +1. CrĂ©er une mission +2. VĂ©rifier les logs : + ``` + Sending to N8N: { missionId: "...", ... } + ``` +3. ✅ `missionId` doit ĂȘtre prĂ©sent + +### Test 2: VĂ©rifier N8N renvoie missionId + +1. VĂ©rifier les logs N8N +2. VĂ©rifier que le node "Save Mission To API" inclut `missionId` +3. ✅ `missionId` doit ĂȘtre dans le body envoyĂ© Ă  `/mission-created` + +### Test 3: VĂ©rifier IDs sont sauvegardĂ©s + +1. CrĂ©er une mission +2. VĂ©rifier les logs : + ``` + === Mission Created Webhook Received === + Looking up mission by ID: ... + Mission updated successfully: { ... } + ``` +3. VĂ©rifier en base : + ```sql + SELECT id, name, giteaRepositoryUrl, leantimeProjectId, + outlineCollectionId, rocketChatChannelId + FROM Mission + WHERE id = '...'; + ``` +4. ✅ Les IDs doivent ĂȘtre prĂ©sents + +### Test 4: VĂ©rifier Suppression + +1. Supprimer une mission avec IDs sauvegardĂ©s +2. VĂ©rifier les logs : + ``` + Sending deletion data to N8N: { + repoName: "creation", + leantimeProjectId: 123, + ... + } + ``` +3. ✅ Les IDs doivent ĂȘtre prĂ©sents (pas vides) + +--- + +## 📝 Checklist + +- [x] Envoyer `missionId` Ă  N8N lors de la crĂ©ation +- [x] AmĂ©liorer recherche de mission dans `/mission-created` +- [ ] **Modifier N8N workflow pour inclure `missionId` dans `/mission-created`** +- [ ] Tester crĂ©ation avec `missionId` +- [ ] Tester sauvegarde des IDs +- [ ] Tester suppression avec IDs sauvegardĂ©s + +--- + +## ⚠ Action ImmĂ©diate Requise + +**Modifier le workflow N8N** pour inclure `missionId` dans le node "Save Mission To API" : + +1. Ouvrir le workflow N8N `NeahMissionCreate` +2. Trouver le node "Save Mission To API" +3. Ajouter `missionId` dans le body : + ```json + { + "missionId": "={{ $node['Process Mission Data'].json.missionId }}", + ... + } + ``` +4. Sauvegarder et activer le workflow + +--- + +**Date**: $(date) +**Version**: 1.1 +**Fichiers ModifiĂ©s**: +- `app/api/missions/route.ts` (ajout missionId dans n8nData) +- `app/api/missions/mission-created/route.ts` (recherche par missionId) + diff --git a/app/api/missions/mission-created/route.ts b/app/api/missions/mission-created/route.ts index d1e83999..b922e757 100644 --- a/app/api/missions/mission-created/route.ts +++ b/app/api/missions/mission-created/route.ts @@ -53,31 +53,45 @@ export async function POST(request: Request) { console.log('Received mission-created data:', JSON.stringify(body, null, 2)); // Validation des champs requis - if (!body.name || !body.creatorId) { + // Prefer missionId if provided, otherwise use name + creatorId + let mission; + + if (body.missionId) { + // ✅ Use missionId if provided (more reliable) + console.log('Looking up mission by ID:', body.missionId); + mission = await prisma.mission.findUnique({ + where: { id: body.missionId } + }); + } else if (body.name && body.creatorId) { + // Fallback to name + creatorId (for backward compatibility) + console.log('Looking up mission by name + creatorId:', { + name: body.name, + creatorId: body.creatorId + }); + mission = await prisma.mission.findFirst({ + where: { + name: body.name, + creatorId: body.creatorId + }, + orderBy: { + createdAt: 'desc' // Prendre la plus rĂ©cente + } + }); + } else { console.error('Missing required fields:', { + hasMissionId: !!body.missionId, hasName: !!body.name, hasCreatorId: !!body.creatorId }); return NextResponse.json( - { error: 'Missing required fields: name and creatorId' }, + { error: 'Missing required fields: missionId OR (name and creatorId)' }, { status: 400 } ); } - // Trouver la mission par name + creatorId - // On cherche la mission la plus rĂ©cente avec ce nom et ce crĂ©ateur - const mission = await prisma.mission.findFirst({ - where: { - name: body.name, - creatorId: body.creatorId - }, - orderBy: { - createdAt: 'desc' // Prendre la plus rĂ©cente - } - }); - if (!mission) { console.error('Mission not found:', { + missionId: body.missionId, name: body.name, creatorId: body.creatorId }); diff --git a/app/api/missions/route.ts b/app/api/missions/route.ts index 9632b764..b3b84eb6 100644 --- a/app/api/missions/route.ts +++ b/app/api/missions/route.ts @@ -370,8 +370,10 @@ export async function POST(request: Request) { const n8nData = { ...body, + missionId: mission.id, // ✅ Send missionId so N8N can return it in /mission-created creatorId: userId, logoPath: logoPath, + logoUrl: logoUrl, // ✅ Send logo URL for N8N to use config: { N8N_API_KEY: process.env.N8N_API_KEY, MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL