Repository: dataforgoodfr/13_pollution_eau
Pull request #135: API 1st version
Author: login: sbsrouteur
Created: 2025-04-01T20:06:42Z | Merged: 2025-04-01T21:59:31Z

PR pour la tache API Endpoint lat/long <> UDI 

Simple end point Lon=xxx&Lat=xxx

Renvoi la ou les UDI qui contiennent la coordonnées. Normalement toujours 1 seule UDI.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit


- **Nouvelles fonctionnalités**
	- Mise en place d'une API permettant de rechercher des données géolocalisées via des coordonnées précises. Les utilisateurs bénéficient désormais d'une validation améliorée et de messages d'erreur clairs en cas de saisie invalide.
	- Amélioration de la gestion des données géospatiales par l'intégration d'une extension dédiée, offrant ainsi des résultats de requêtes plus précis et fiables.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/pipelines/tasks/trim_database_for_website.py b/pipelines/tasks/trim_database_for_website.py
index f905a7d5..ca4ca092 100644
--- a/pipelines/tasks/trim_database_for_website.py
+++ b/pipelines/tasks/trim_database_for_website.py
@@ -28,6 +28,7 @@
     "web__resultats_communes",
     "web__resultats_udi",
     "cog_communes",
+    "atlasante_udi",
 ]
 
 DEFAULT_OUTPUT_FILE = "database/database_website.duckdb"
diff --git a/webapp/app/api/udi/find/route.tsx b/webapp/app/api/udi/find/route.tsx
new file mode 100644
index 00000000..aaa653a4
--- /dev/null
+++ b/webapp/app/api/udi/find/route.tsx
@@ -0,0 +1,74 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  const { searchParams } = new URL(req.url);
+  const lonParam = searchParams.get("lon");
+  const latParam = searchParams.get("lat");
+
+  if (lonParam == null || latParam == null) {
+    return NextResponse.json(
+      { message: "Paramètres manquants: lon et lat sont requis" },
+      { status: 400 },
+    );
+  }
+  const lon = parseFloat(lonParam);
+  const lat = parseFloat(latParam);
+
+  if (
+    isNaN(lon) ||
+    isNaN(lat) ||
+    lon < -180 ||
+    lon > 180 ||
+    lat < -90 ||
+    lat > 90
+  ) {
+    return NextResponse.json(
+      { message: "Paramètres invalides" },
+      { status: 400 },
+    );
+  }
+
+  const connection = await db.connect();
+  try {
+    await connection.run("LOAD spatial;");
+
+    const prepared = await connection.prepare(`
+      SELECT code_udi
+      FROM atlasante_udi
+      WHERE ST_Contains(geom, ST_GeomFromText($1::VARCHAR))
+      ORDER BY udi_pop DESC
+      LIMIT 1
+    `);
+
+    const point = `POINT(${lon} ${lat})`;
+    prepared.bindVarchar(1, point);
+
+    const result = await prepared.runAndReadAll();
+
+    if (result.currentRowCount > 0) {
+      return NextResponse.json(
+        { id: result.getRowObjectsJson()[0]["code_udi"] },
+        { status: 200 },
+      );
+    } else {
+      return NextResponse.json(
+        { message: "Aucune UDI ne correspond à ces coordonnées" },
+        { status: 404 },
+      );
+    }
+  } catch (error) {
+    console.error("Erreur de base de données:", error);
+    return NextResponse.json(
+      {
+        message:
+          "Une erreur interne s'est produite. Veuillez réessayer ultérieurement.",
+      },
+      { status: 500 },
+    );
+  } finally {
+    await connection.close();
+  }
+}
diff --git a/webapp/app/lib/duckdb.ts b/webapp/app/lib/duckdb.ts
index 27206cba..e42f050c 100644
--- a/webapp/app/lib/duckdb.ts
+++ b/webapp/app/lib/duckdb.ts
@@ -23,4 +23,9 @@ const db = await DuckDBInstance.create(dbFilePath, {
   threads: "4",
 });
 
+// Load the geospatial extension
+const connection = await db.connect();
+await connection.run("INSTALL spatial;");
+await connection.run("LOAD spatial;");
+
 export default db;


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>webapp/app/api/UDILookup/route.tsx (5)</summary><blockquote>

`12-26`: **Utilisation des codes HTTP à revoir**

Bonjour ! J'ai remarqué que vous utilisez le code d'état HTTP 403 (Forbidden) pour les paramètres invalides. Ce code est généralement réservé aux problèmes d'autorisation, alors qu'un code 400 (Bad Request) serait plus approprié dans ce contexte.


```diff
- { status: 403 },
+ { status: 400 },
```

---

`38-38`: **Log de débogage à supprimer**

Ce console.log devrait être supprimé avant la mise en production, ou remplacé par un système de logging plus approprié.


```diff
- console.log(result);
+ // Utiliser un système de logging structuré si nécessaire
+ // logger.debug('Query result', { resultCount: result.currentRowCount });
```

---

`45-48`: **Code d'état HTTP pour "ressource non trouvée"**

Pour indiquer qu'aucune UDI ne correspond aux coordonnées, le code 404 (Not Found) serait plus approprié que 400 (Bad Request).


```diff
- { status: 400 },
+ { status: 404 },
```

---

`1-5`: **Extension de fichier inappropriée pour du code Next.js API**

J'ai remarqué que vous utilisez l'extension `.tsx` pour ce fichier alors qu'il ne contient pas de syntaxe JSX. Pour les routes API Next.js, l'extension `.ts` est plus appropriée.


Renommez le fichier de `route.tsx` à `route.ts`.

---

`6-54`: **Suggestion d'optimisation de la structure du code**

Pour améliorer la lisibilité et la maintenance du code, je suggère de restructurer la fonction GET en séparant les responsabilités : validation des paramètres, exécution de la requête, et formatage de la réponse.


```typescript
export async function GET(req: NextRequest) {
  try {
    // 1. Extraction et validation des paramètres
    const { lon, lat, error } = extractAndValidateParams(req);
    if (error) {
      return NextResponse.json(
        { message: error },
        { status: 400 }
      );
    }

    // 2. Exécution de la requête
    const result = await queryUDIByCoordinates(lon, lat);

    // 3. Formatage et envoi de la réponse
    if (result.currentRowCount) {
      return NextResponse.json(
        { udis: result.getRowObjectsJson() },
        { status: 200 }
      );
    } else {
      return NextResponse.json(
        { message: "Aucune UDI ne correspond à ces coordonnées" },
        { status: 404 }
      );
    }
  } catch (error) {
    console.error("Erreur:", error);
    return NextResponse.json(
      { message: "Une erreur interne s'est produite" },
      { status: 500 }
    );
  }
}

function extractAndValidateParams(req: NextRequest) {
  const { searchParams } = new URL(req.url);
  const lonParam = searchParams.get("Lon");
  const latParam = searchParams.get("Lat");

  if (lonParam == null || latParam == null) {
    return { error: "Paramètres manquants" };
  }

  const lon = parseFloat(lonParam);
  const lat = parseFloat(latParam);

  if (isNaN(lon) || isNaN(lat)) {
    return { error: "Paramètres invalides" };
  }

  // Validation supplémentaire des coordonnées
  if (Math.abs(lon) > 180 || Math.abs(lat) > 90) {
    return { error: "Coordonnées hors limites" };
  }

  return { lon, lat };
}

async function queryUDIByCoordinates(lon: number, lat: number) {
  const connection = await db.connect();
  await connection.run("INSTALL spatial;LOAD spatial;");
  await connection.run("INSTALL json;LOAD json;");
  
  const querySql = `SELECT * EXCLUDE(geom) FROM atlasante_udi 
    WHERE ST_Contains(geom, ST_GeomFromText('POINT(${lon} ${lat})'))`;
  
  return await connection.runAndReadAll(querySql);
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between b27c065a9fc0f2928aa97149d272064b058d77de and 17352da3a4a994c0ad87766ee20aa48f58d27f71.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `webapp/app/api/UDILookup/route.tsx` (1 hunks)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T20:09:13Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>webapp/app/api/UDILookup/route.tsx (4)</summary><blockquote>

`12-17`: **Code HTTP inapproprié pour les paramètres invalides**

Le code de statut HTTP 403 (Forbidden) n'est pas approprié pour signaler des paramètres manquants. Le code 400 (Bad Request) serait plus adapté à cette situation.

De plus, le message d'erreur est en anglais alors que les autres messages d'erreur dans le code sont en français. Pour maintenir la cohérence, il serait préférable d'utiliser un message en français.

```diff
  if (lonParam == null || latParam == null) {
    return NextResponse.json(
-     { message: "Invalid parameters" },
-     { status: 403 },
+     { message: "Paramètres invalides ou manquants" },
+     { status: 400 },
    );
  }
```

---

`28-30`: **Installation des extensions à optimiser**

L'installation des extensions à chaque requête peut être inefficace, surtout si l'API reçoit de nombreuses requêtes. Il serait préférable d'installer ces extensions une seule fois au démarrage de l'application.

Envisagez de déplacer ces installations dans un fichier d'initialisation de la base de données qui serait exécuté au démarrage du serveur, ou utilisez un système de vérification qui n'installera les extensions que si elles ne sont pas déjà chargées.

---

`38-38`: **Supprimer les logs de débogage**

Les déclarations `console.log` sont utiles pendant le développement mais devraient être supprimées ou remplacées par un système de journalisation configurable en production.

```diff
- console.log(result);
+ // Utiliser un système de journalisation configurable en production si nécessaire
```

---

`6-54`: **Structurer la fonction en sous-fonctions**

La fonction GET actuelle gère plusieurs responsabilités : validation des paramètres, exécution de requêtes et formatage des réponses. Pour améliorer la lisibilité et la maintenabilité, considérez la décomposition en sous-fonctions.

Voici comment vous pourriez structurer votre code :

```typescript
// Fonction de validation des paramètres
function validateParams(lonParam: string | null, latParam: string | null) {
  if (lonParam === null || latParam === null) {
    return { valid: false, error: "Paramètres manquants", status: 400 };
  }
  
  const lon = parseFloat(lonParam);
  const lat = parseFloat(latParam);
  
  if (isNaN(lon) || isNaN(lat) || lon < -180 || lon > 180 || lat < -90 || lat > 90) {
    return { valid: false, error: "Coordonnées géographiques invalides", status: 400 };
  }
  
  return { valid: true, lon, lat };
}

// Fonction principale
export async function GET(req: NextRequest) {
  let connection = null;
  
  try {
    // Validation des paramètres
    const { searchParams } = new URL(req.url);
    const validation = validateParams(searchParams.get("Lon"), searchParams.get("Lat"));
    
    if (!validation.valid) {
      return NextResponse.json(
        { success: false, message: validation.error },
        { status: validation.status }
      );
    }
    
    // Connexion et requête à la base de données
    connection = await db.connect();
    await connection.run("INSTALL spatial;LOAD spatial;");
    await connection.run("INSTALL json;LOAD json;");
    
    const { lon, lat } = validation;
    const querySql = `SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText('POINT(${lon} ${lat})'))`;
    const result = await connection.runAndReadAll(querySql);
    
    // Formatage de la réponse
    if (result.currentRowCount) {
      return NextResponse.json(
        { success: true, udis: result.getRowObjectsJson() },
        { status: 200 }
      );
    } else {
      return NextResponse.json(
        { success: false, udis: [], message: "Aucune UDI ne correspond à ces coordonnées" },
        { status: 404 }
      );
    }
  } catch (error) {
    console.error("Erreur de base de données:", error);
    return NextResponse.json(
      { success: false, message: "Une erreur interne s'est produite. Veuillez réessayer ultérieurement." },
      { status: 500 }
    );
  } finally {
    if (connection) {
      await connection.close();
    }
  }
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 17352da3a4a994c0ad87766ee20aa48f58d27f71 and 059d45e508dbcda122b3544a3ae2741d5338908e.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `webapp/app/api/UDILookup/route.tsx` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>webapp/app/api/UDILookup/route.tsx (2)</summary>

`31-36`: **Risque d'injection SQL à corriger**

Attention à la construction de requête SQL par concaténation ! Même si les valeurs sont des nombres validés par parseFloat, il serait plus sûr d'utiliser des paramètres préparés si DuckDB les supporte, ou d'ajouter une validation supplémentaire.

---

`50-53`: **Gestion des erreurs à améliorer**

La gestion des erreurs expose directement le message d'erreur dans la réponse, ce qui pourrait révéler des informations sensibles sur l'infrastructure. De plus, le code 403 n'est pas approprié pour toutes les erreurs.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T20:12:41Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>webapp/app/api/UDILookup/route.tsx (5)</summary><blockquote>

`1-5`: **Documentation peut être améliorée**

Le commentaire initial est trop générique. Il serait utile d'ajouter une description plus détaillée expliquant le but spécifique de cette API et son fonctionnement.

```diff
-// an api route fetching data
+/**
+ * API pour la recherche d'UDI par coordonnées géographiques
+ * 
+ * Cette route API permet de récupérer l'UDI correspondant à des coordonnées
+ * géographiques (longitude et latitude) spécifiées dans les paramètres de requête.
+ */
```

---

`28-30`: **Optimisation du chargement des extensions**

Installer et charger les extensions à chaque requête est inefficace. Il serait préférable de le faire au démarrage de l'application ou de vérifier si elles sont déjà chargées.

```diff
const connection = await db.connect();
-await connection.run("INSTALL spatial;LOAD spatial;");
-await connection.run("INSTALL json;LOAD json;");
+// Ces extensions devraient être installées une seule fois au démarrage de l'application
+// Si cela n'est pas possible, ajoutez une vérification:
+await connection.run("LOAD spatial; LOAD json;");
```

---

`38-38`: **Supprimer le console.log de débogage**

Les instructions de débogage ne devraient pas être présentes dans le code de production.

```diff
-console.log(result);
```

---

`6-60`: **Tests automatisés manquants**

Cette API manque de tests unitaires pour vérifier son bon fonctionnement. Je recommande d'ajouter des tests qui vérifient:
1. La validation des paramètres
2. Le comportement en cas de coordonnées invalides
3. La gestion correcte des cas où aucune UDI n'est trouvée
4. Le formatage correct des réponses


Souhaiteriez-vous que je vous aide à créer des tests unitaires pour cette API? Je peux vous proposer un exemple de structure de test avec Jest ou un autre framework.

---

`6-60`: **Documentation OpenAPI manquante**

Il serait utile d'ajouter une documentation OpenAPI (Swagger) pour cette API afin de faciliter son utilisation par d'autres développeurs. Cela permettrait également de documenter clairement les paramètres attendus et les différentes réponses possibles.


Je vous conseille d'ajouter une spécification OpenAPI pour cette route. Cela pourrait s'intégrer dans une documentation plus large de votre API, ce qui serait particulièrement utile pour un projet collaboratif comme Data For Good.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 059d45e508dbcda122b3544a3ae2741d5338908e and 9def67e3243c51e9ff20ffb24344c52bd6e8fd42.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `webapp/app/api/UDILookup/route.tsx` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>webapp/app/api/UDILookup/route.tsx (4)</summary>

`18-26`: **Validation des coordonnées à améliorer**

La validation actuelle vérifie uniquement si les valeurs peuvent être converties en nombres, mais ne vérifie pas si ces nombres sont dans les plages valides pour des coordonnées géographiques.

```diff
const lon = parseFloat(lonParam);
const lat = parseFloat(latParam);

-if (isNaN(lon) || isNaN(lat)) {
+if (isNaN(lon) || isNaN(lat) || lon < -180 || lon > 180 || lat < -90 || lat > 90) {
  return NextResponse.json(
-   { message: "Invalid parameters" },
-   { status: 403 },
+   { success: false, message: "Coordonnées géographiques invalides" },
+   { status: 400 },
  );
}
```

---

`31-36`: **Risque d'injection SQL à corriger**

Même si les valeurs sont des nombres validés par parseFloat, il est préférable d'utiliser la syntaxe template literals avec validation supplémentaire.

```diff
-const querySql =
-  "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
-  lon +
-  " " +
-  lat +
-  ")' ))";
+// Utiliser une fonction de validation supplémentaire
+function isValidCoordinate(coord, isLongitude = true) {
+  const maxValue = isLongitude ? 180 : 90;
+  return typeof coord === 'number' && !isNaN(coord) && 
+         Math.abs(coord) <= maxValue;
+}
+
+if (!isValidCoordinate(lon, true) || !isValidCoordinate(lat, false)) {
+  return NextResponse.json(
+    { success: false, message: "Coordonnées invalides" },
+    { status: 400 }
+  );
+}
+
+const querySql = `SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText('POINT(${lon} ${lat})'))`;
```

---

`28-49`: **Gestion des connexions à la base de données**

La connexion à la base de données n'est jamais fermée, ce qui pourrait entraîner des fuites de ressources. Il faut ajouter un bloc `finally` pour s'assurer que la connexion est toujours fermée.

```diff
const connection = await db.connect();
-await connection.run("INSTALL spatial;LOAD spatial;");
-await connection.run("INSTALL json;LOAD json;");
-const querySql =
-  "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
-  lon +
-  " " +
-  lat +
-  ")' ))";
-const result = await connection.runAndReadAll(querySql);
-console.log(result);
-if (result.currentRowCount) {
-  return NextResponse.json(
-    { udis: result.getRowObjectsJson() },
-    { status: 200 },
-  );
-} else {
-  return NextResponse.json(
-    { message: "Aucune udi ne correspond à ces coordonnées" },
-    { status: 400 },
-  );
-}
+try {
+  await connection.run("LOAD spatial; LOAD json;");
+  
+  const querySql = `SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText('POINT(${lon} ${lat})'))`;
+  const result = await connection.runAndReadAll(querySql);
+  
+  if (result.currentRowCount) {
+    return NextResponse.json(
+      { success: true, udis: result.getRowObjectsJson() },
+      { status: 200 },
+    );
+  } else {
+    return NextResponse.json(
+      { success: false, udis: [], message: "Aucune UDI ne correspond à ces coordonnées" },
+      { status: 404 },
+    );
+  }
+} finally {
+  await connection.close();
+}
```

---

`39-49`: **Code HTTP inapproprié pour l'absence de résultats**

Le code de statut HTTP 400 (Bad Request) n'est pas approprié lorsqu'aucune UDI n'est trouvée. Le code 404 (Not Found) serait plus adapté. De plus, la structure de réponse devrait être cohérente entre les cas de succès et d'erreur.

```diff
if (result.currentRowCount) {
  return NextResponse.json(
-   { udis: result.getRowObjectsJson() },
+   { success: true, udis: result.getRowObjectsJson() },
    { status: 200 },
  );
} else {
  return NextResponse.json(
-   { message: "Aucune udi ne correspond à ces coordonnées" },
-   { status: 400 },
+   { success: false, udis: [], message: "Aucune UDI ne correspond à ces coordonnées" },
+   { status: 404 },
  );
}
```

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T20:16:58Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>webapp/app/lib/duckdb.ts (1)</summary><blockquote>

`26-30`: **Amélioration de la gestion de connexion pour l'extension spatiale**

L'ajout de l'extension spatiale est une bonne initiative pour permettre les requêtes géospatiales. Cependant, il y a quelques points à améliorer:

1. La connexion créée n'est jamais fermée explicitement.
2. Il n'y a pas de gestion d'erreurs spécifique pour l'installation ou le chargement de l'extension.
3. L'installation de l'extension est une opération d'écriture, mais la base est configurée en mode lecture seule.


```diff
// Load the geospatial extension
const connection = await db.connect();
-await connection.run("INSTALL spatial;");
-await connection.run("LOAD spatial;");
+try {
+  // Vérifier si l'extension est déjà installée
+  const result = await connection.runAndReadAll("SELECT installed_version FROM duckdb_extensions() WHERE extension_name = 'spatial'");
+  const isInstalled = result.currentRowCount > 0;
+  
+  if (!isInstalled) {
+    console.log("Installation de l'extension spatiale...");
+    await connection.run("INSTALL spatial;");
+  }
+  
+  await connection.run("LOAD spatial;");
+  console.log("Extension spatiale chargée avec succès");
+} catch (error) {
+  console.error("Erreur lors du chargement de l'extension spatiale:", error);
+  throw error;
+} finally {
+  await connection.close();
+}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9def67e3243c51e9ff20ffb24344c52bd6e8fd42 and 36f4698cce9e22f51862873f182e5558cebfb1a7.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `webapp/app/api/udi/find/route.tsx` (1 hunks)
* `webapp/app/lib/duckdb.ts` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>webapp/app/api/udi/find/route.tsx (3)</summary>

`6-10`: **LGTM: Déclaration de la fonction et récupération des paramètres**

La déclaration de la fonction GET et la récupération des paramètres sont bien implémentées. Vous utilisez correctement l'objet URL pour extraire les paramètres de recherche.

---

`11-32`: **LGTM: Validation des paramètres**

Bonne validation des paramètres avec vérification de leur présence et de leur plage de valeurs. Les messages d'erreur sont clairs et pertinents.

---

`56-67`: **LGTM: Gestion d'erreurs et fermeture de connexion**

La gestion des erreurs et la fermeture de la connexion dans un bloc finally sont bien implémentées. C'est une bonne pratique pour garantir la libération des ressources.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T21:34:16Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>webapp/app/api/udi/find/route.tsx (3)</summary><blockquote>

`1-5`: **Améliorez la documentation de l'API**

Bonjour ! La description actuelle "an api route fetching data" est très générique. Pour faciliter la compréhension et la maintenance par l'équipe de Data For Good, je recommande d'ajouter une documentation plus détaillée avec JSDoc qui explique :
- L'objectif de cette API
- Les paramètres attendus et leur format
- Les différentes réponses possibles et leurs codes HTTP

```diff
- // an api route fetching data
+ /**
+  * API permettant de récupérer l'UDI correspondant à des coordonnées géographiques.
+  * 
+  * @param {string} lon - Longitude comprise entre -180 et 180
+  * @param {string} lat - Latitude comprise entre -90 et 90
+  * 
+  * @returns {Object} Réponse contenant soit l'UDI correspondante, soit un message d'erreur
+  * @status 200 - UDI trouvée avec succès
+  * @status 400 - Paramètres manquants ou invalides
+  * @status 404 - Aucune UDI ne correspond aux coordonnées
+  * @status 500 - Erreur serveur
+  */
```

---

`11-32`: **Améliorez la clarté des messages d'erreur**

Les validations sont bien structurées, mais les messages d'erreur pourraient être plus informatifs pour aider les utilisateurs de l'API à comprendre exactement ce qui est incorrect.

```diff
  if (lonParam == null || latParam == null) {
    return NextResponse.json(
-      { message: "Paramètres manquants: lon et lat sont requis" },
+      { 
+        message: "Paramètres manquants",
+        detail: {
+          lon: lonParam === null ? "manquant" : "présent",
+          lat: latParam === null ? "manquant" : "présent"
+        }
+      },
      { status: 400 },
    );
  }
  const lon = parseFloat(lonParam);
  const lat = parseFloat(latParam);

  if (
    isNaN(lon) ||
    isNaN(lat) ||
    lon < -180 ||
    lon > 180 ||
    lat < -90 ||
    lat > 90
  ) {
    return NextResponse.json(
-      { message: "Paramètres invalides" },
+      { 
+        message: "Paramètres invalides",
+        detail: {
+          lon: isNaN(lon) ? "format invalide" : (lon < -180 || lon > 180 ? "doit être entre -180 et 180" : "valide"),
+          lat: isNaN(lat) ? "format invalide" : (lat < -90 || lat > 90 ? "doit être entre -90 et 90" : "valide")
+        }
+      },
      { status: 400 },
    );
  }
```

---

`62-71`: **Améliorez la journalisation des erreurs**

La journalisation actuelle des erreurs est basique. Pour faciliter le débogage des problèmes en production, il serait utile d'ajouter plus de contexte :

```diff
  } catch (error) {
-    console.error("Erreur de base de données:", error);
+    console.error("Erreur lors de la recherche d'UDI par coordonnées:", {
+      longitude: lon,
+      latitude: lat, 
+      error: error instanceof Error ? error.message : String(error),
+      stack: error instanceof Error ? error.stack : undefined
+    });
    return NextResponse.json(
      {
        message:
          "Une erreur interne s'est produite. Veuillez réessayer ultérieurement.",
      },
      { status: 500 },
    );
```

Cette approche fournit plus de contexte pour comprendre l'erreur tout en maintenant une réponse générique pour l'utilisateur final.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 36f4698cce9e22f51862873f182e5558cebfb1a7 and 5796752350f1f3fe6e3d14eca4af75f0bb73e57c.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `webapp/app/api/udi/find/route.tsx` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>webapp/app/api/udi/find/route.tsx (3)</summary>

`38-44`: **Bonne utilisation des requêtes préparées**

Excellente implémentation de la requête préparée qui résout le problème d'injection SQL mentionné dans les commentaires précédents. La structure de la requête avec `ORDER BY udi_pop DESC LIMIT 1` est également judicieuse pour garantir de récupérer l'UDI la plus pertinente.

Petite suggestion pour améliorer la lisibilité du SQL :

```diff
    const prepared = await connection.prepare(`
      SELECT code_udi
      FROM atlasante_udi
      WHERE ST_Contains(geom, ST_GeomFromText($1::VARCHAR))
      ORDER BY udi_pop DESC
      LIMIT 1
    `);
```

---

`51-61`: **Code d'erreur 404 approprié**

Merci d'avoir implémenté le code d'erreur 404 pour le cas où aucune UDI ne correspond aux coordonnées, comme suggéré dans les commentaires précédents. C'est sémantiquement correct et conforme aux bonnes pratiques REST.

Le format de réponse est également bien structuré et cohérent.

---

`71-74`: **Excellente gestion des ressources de connexion**

L'utilisation du bloc `finally` pour fermer la connexion à la base de données est une bonne pratique qui garantit la libération des ressources, même en cas d'erreur. C'est particulièrement important pour éviter les fuites de ressources dans un environnement de production.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T21:56:43Z

Review comments:
diff_hunk: @@ -0,0 +1,54 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  try {
+    const { searchParams } = new URL(req.url);
+    const lonParam = searchParams.get("Lon");
+    const latParam = searchParams.get("Lat");
+
+    if (lonParam == null || latParam == null) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+    const lon = parseFloat(lonParam);
+    const lat = parseFloat(latParam);
+
+    if (isNaN(lon) || isNaN(lat)) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+    
+    const connection = await db.connect();
+    await connection.run("INSTALL spatial;LOAD spatial;");
+    await connection.run("INSTALL json;LOAD json;");
+    const querySql =
+      "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
+      lon +
+      " " +
+      lat +
+      ")' ))";
+    const result = await connection.runAndReadAll(querySql);
+    console.log(result);
+    if (result.currentRowCount) {
+      return NextResponse.json(
+        { udis: result.getRowObjectsJson() },
+        { status: 200 },
+      );
+    } else {
+      return NextResponse.json(
+        { message: "Aucune udi ne correspond à ces coordonnées" },
+        { status: 400 },
+      );
+    }
+  } catch (error) {
+    console.log("db error", error);
+    return NextResponse.json({ message: error }, { status: 403 });
+  }
path: webapp/app/api/UDILookup/route.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Gestion des erreurs à améliorer**

La gestion des erreurs expose directement le message d'erreur dans la réponse, ce qui pourrait révéler des informations sensibles sur l'infrastructure. De plus, le code 403 n'est pas approprié pour toutes les erreurs.


```diff
- console.log("db error", error);
- return NextResponse.json({ message: error }, { status: 403 });
+ console.error("Erreur de base de données:", error);
+ return NextResponse.json(
+   { message: "Une erreur interne s'est produite. Veuillez réessayer ultérieurement." },
+   { status: 500 }
+ );
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  } catch (error) {
-    console.log("db error", error);
-    return NextResponse.json({ message: error }, { status: 403 });
+    console.error("Erreur de base de données:", error);
+    return NextResponse.json(
+      { message: "Une erreur interne s'est produite. Veuillez réessayer ultérieurement." },
+      { status: 500 }
+    );
  }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 9def67e
created_at: 2025-04-01T20:09:12Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  try {
+    const { searchParams } = new URL(req.url);
+    const lonParam = searchParams.get("Lon");
+    const latParam = searchParams.get("Lat");
+
+    if (lonParam == null || latParam == null) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+    const lon = parseFloat(lonParam);
+    const lat = parseFloat(latParam);
+
+    if (isNaN(lon) || isNaN(lat)) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+    
+    const connection = await db.connect();
+    await connection.run("INSTALL spatial;LOAD spatial;");
+    await connection.run("INSTALL json;LOAD json;");
+    const querySql =
+      "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
+      lon +
+      " " +
+      lat +
+      ")' ))";
path: webapp/app/api/UDILookup/route.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Risque d'injection SQL à corriger**

Attention à la construction de requête SQL par concaténation ! Même si les valeurs sont des nombres validés par parseFloat, il serait plus sûr d'utiliser des paramètres préparés si DuckDB les supporte, ou d'ajouter une validation supplémentaire.


Voici une suggestion pour améliorer la sécurité :
```diff
- const querySql =
-   "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
-   lon +
-   " " +
-   lat +
-   ")' ))";
+ // Utiliser une fonction de validation supplémentaire
+ function isValidCoordinate(coord) {
+   return typeof coord === 'number' && !isNaN(coord) && 
+          Math.abs(coord) <= 180; // Valeur maximale pour les coordonnées
+ }
+ 
+ if (!isValidCoordinate(lon) || !isValidCoordinate(lat)) {
+   return NextResponse.json(
+     { message: "Coordonnées invalides" },
+     { status: 400 }
+   );
+ }
+ 
+ const querySql = `SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText('POINT(${lon} ${lat})'))`;
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    // Utiliser une fonction de validation supplémentaire
    function isValidCoordinate(coord) {
      return typeof coord === 'number' && !isNaN(coord) && 
             Math.abs(coord) <= 180; // Valeur maximale pour les coordonnées
    }
    
    if (!isValidCoordinate(lon) || !isValidCoordinate(lat)) {
      return NextResponse.json(
        { message: "Coordonnées invalides" },
        { status: 400 }
      );
    }
    
    const querySql = `SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText('POINT(${lon} ${lat})'))`;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-01T20:09:12Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  try {
+    const { searchParams } = new URL(req.url);
+    const lonParam = searchParams.get("Lon");
+    const latParam = searchParams.get("Lat");
+
+    if (lonParam == null || latParam == null) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+    const lon = parseFloat(lonParam);
+    const lat = parseFloat(latParam);
+
+    if (isNaN(lon) || isNaN(lat)) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
path: webapp/app/api/UDILookup/route.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Validation des coordonnées à améliorer**

La validation actuelle vérifie uniquement si les valeurs peuvent être converties en nombres, mais ne vérifie pas si ces nombres sont dans les plages valides pour des coordonnées géographiques (longitude entre -180 et 180, latitude entre -90 et 90).

De plus, comme pour le message précédent, le code 403 n'est pas approprié et le message est en anglais.

```diff
  const lon = parseFloat(lonParam);
  const lat = parseFloat(latParam);

- if (isNaN(lon) || isNaN(lat)) {
+ if (isNaN(lon) || isNaN(lat) || lon < -180 || lon > 180 || lat < -90 || lat > 90) {
    return NextResponse.json(
-     { message: "Invalid parameters" },
-     { status: 403 },
+     { message: "Coordonnées géographiques invalides" },
+     { status: 400 },
    );
  }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    const lon = parseFloat(lonParam);
    const lat = parseFloat(latParam);

    if (isNaN(lon) || isNaN(lat) || lon < -180 || lon > 180 || lat < -90 || lat > 90) {
      return NextResponse.json(
        { message: "Coordonnées géographiques invalides" },
        { status: 400 },
      );
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-01T20:12:41Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  try {
+    const { searchParams } = new URL(req.url);
+    const lonParam = searchParams.get("Lon");
+    const latParam = searchParams.get("Lat");
+
+    if (lonParam == null || latParam == null) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+    const lon = parseFloat(lonParam);
+    const lat = parseFloat(latParam);
+
+    if (isNaN(lon) || isNaN(lat)) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+
+    const connection = await db.connect();
+    await connection.run("INSTALL spatial;LOAD spatial;");
+    await connection.run("INSTALL json;LOAD json;");
+    const querySql =
+      "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
+      lon +
+      " " +
+      lat +
+      ")' ))";
+    const result = await connection.runAndReadAll(querySql);
+    console.log(result);
+    if (result.currentRowCount) {
+      return NextResponse.json(
+        { udis: result.getRowObjectsJson() },
+        { status: 200 },
+      );
+    } else {
+      return NextResponse.json(
+        { message: "Aucune udi ne correspond à ces coordonnées" },
+        { status: 400 },
+      );
+    }
path: webapp/app/api/UDILookup/route.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Code HTTP inapproprié pour l'absence de résultats**

Le code de statut HTTP 400 (Bad Request) n'est pas approprié lorsqu'aucune UDI n'est trouvée. Un code 404 (Not Found) serait plus adapté, ou même un code 200 avec un tableau vide.

De plus, la structure de réponse n'est pas cohérente entre les cas de succès (`udis`) et d'erreur (`message`). Une structure cohérente faciliterait l'utilisation de l'API.

```diff
  if (result.currentRowCount) {
    return NextResponse.json(
-     { udis: result.getRowObjectsJson() },
+     { success: true, udis: result.getRowObjectsJson() },
      { status: 200 },
    );
  } else {
    return NextResponse.json(
-     { message: "Aucune udi ne correspond à ces coordonnées" },
-     { status: 400 },
+     { success: false, udis: [], message: "Aucune UDI ne correspond à ces coordonnées" },
+     { status: 404 },
    );
  }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    if (result.currentRowCount) {
      return NextResponse.json(
        { success: true, udis: result.getRowObjectsJson() },
        { status: 200 },
      );
    } else {
      return NextResponse.json(
        { success: false, udis: [], message: "Aucune UDI ne correspond à ces coordonnées" },
        { status: 404 },
      );
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-01T20:12:41Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  try {
+    const { searchParams } = new URL(req.url);
+    const lonParam = searchParams.get("Lon");
+    const latParam = searchParams.get("Lat");
+
+    if (lonParam == null || latParam == null) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+    const lon = parseFloat(lonParam);
+    const lat = parseFloat(latParam);
+
+    if (isNaN(lon) || isNaN(lat)) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
+
+    const connection = await db.connect();
+    await connection.run("INSTALL spatial;LOAD spatial;");
+    await connection.run("INSTALL json;LOAD json;");
+    const querySql =
+      "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
+      lon +
+      " " +
+      lat +
+      ")' ))";
+    const result = await connection.runAndReadAll(querySql);
+    console.log(result);
+    if (result.currentRowCount) {
+      return NextResponse.json(
+        { udis: result.getRowObjectsJson() },
+        { status: 200 },
+      );
+    } else {
+      return NextResponse.json(
+        { message: "Aucune udi ne correspond à ces coordonnées" },
+        { status: 400 },
+      );
+    }
+  } catch (error) {
+    console.log("db error", error);
+    return NextResponse.json({ message: error }, { status: 403 });
+  }
+}
path: webapp/app/api/UDILookup/route.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Gestion des connexions à la base de données**

La connexion à la base de données est ouverte mais n'est jamais fermée, ce qui pourrait entraîner des fuites de connexions. Il est important de fermer la connexion après utilisation, idéalement dans un bloc `finally`.

```diff
  try {
    const connection = await db.connect();
-   await connection.run("INSTALL spatial;LOAD spatial;");
-   await connection.run("INSTALL json;LOAD json;");
-   const querySql =
-     "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
-     lon +
-     " " +
-     lat +
-     ")' ))";
-   const result = await connection.runAndReadAll(querySql);
-   console.log(result);
-   if (result.currentRowCount) {
-     return NextResponse.json(
-       { udis: result.getRowObjectsJson() },
-       { status: 200 },
-     );
-   } else {
-     return NextResponse.json(
-       { message: "Aucune udi ne correspond à ces coordonnées" },
-       { status: 400 },
-     );
-   }
+   try {
+     await connection.run("INSTALL spatial;LOAD spatial;");
+     await connection.run("INSTALL json;LOAD json;");
+     const querySql = `SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText('POINT(${lon} ${lat})'))`;
+     const result = await connection.runAndReadAll(querySql);
+     
+     if (result.currentRowCount) {
+       return NextResponse.json(
+         { success: true, udis: result.getRowObjectsJson() },
+         { status: 200 },
+       );
+     } else {
+       return NextResponse.json(
+         { success: false, udis: [], message: "Aucune UDI ne correspond à ces coordonnées" },
+         { status: 404 },
+       );
+     }
+   } finally {
+     await connection.close();
+   }
  } catch (error) {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  try {
    const connection = await db.connect();
    try {
      await connection.run("INSTALL spatial;LOAD spatial;");
      await connection.run("INSTALL json;LOAD json;");
      const querySql = `SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText('POINT(${lon} ${lat})'))`;
      const result = await connection.runAndReadAll(querySql);
      
      if (result.currentRowCount) {
        return NextResponse.json(
          { success: true, udis: result.getRowObjectsJson() },
          { status: 200 },
        );
      } else {
        return NextResponse.json(
          { success: false, udis: [], message: "Aucune UDI ne correspond à ces coordonnées" },
          { status: 404 },
        );
      }
    } finally {
      await connection.close();
    }
  } catch (error) {
    console.log("db error", error);
    return NextResponse.json({ message: error }, { status: 403 });
  }
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-01T20:12:41Z
author_association: NONE
diff_hunk: @@ -0,0 +1,60 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  try {
+    const { searchParams } = new URL(req.url);
+    const lonParam = searchParams.get("Lon");
+    const latParam = searchParams.get("Lat");
+
+    if (lonParam == null || latParam == null) {
+      return NextResponse.json(
+        { message: "Invalid parameters" },
+        { status: 403 },
+      );
+    }
path: webapp/app/api/UDILookup/route.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Code HTTP inapproprié et message d'erreur à améliorer**

Le code de statut HTTP 403 (Forbidden) n'est pas approprié pour des paramètres manquants. Le code 400 (Bad Request) serait plus adapté. De plus, le message d'erreur est en anglais alors que les autres messages sont en français.

```diff
if (lonParam == null || latParam == null) {
  return NextResponse.json(
-   { message: "Invalid parameters" },
-   { status: 403 },
+   { success: false, message: "Paramètres manquants: Lon et Lat sont requis" },
+   { status: 400 },
  );
}
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    if (lonParam == null || latParam == null) {
      return NextResponse.json(
        { success: false, message: "Paramètres manquants: Lon et Lat sont requis" },
        { status: 400 },
      );
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-01T20:16:57Z
author_association: NONE
diff_hunk: @@ -0,0 +1,68 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  const { searchParams } = new URL(req.url);
+  const lonParam = searchParams.get("lon");
+  const latParam = searchParams.get("lat");
+
+  if (lonParam == null || latParam == null) {
+    return NextResponse.json(
+      { message: "Paramètres manquants: lon et lat sont requis" },
+      { status: 400 },
+    );
+  }
+  const lon = parseFloat(lonParam);
+  const lat = parseFloat(latParam);
+
+  if (
+    isNaN(lon) ||
+    isNaN(lat) ||
+    lon < -180 ||
+    lon > 180 ||
+    lat < -90 ||
+    lat > 90
+  ) {
+    return NextResponse.json(
+      { message: "Paramètres invalides" },
+      { status: 400 },
+    );
+  }
+
+  const connection = await db.connect();
+  try {
+    await connection.run("LOAD spatial;");
+    const querySql =
+      "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
+      lon +
+      " " +
+      lat +
+      ")' ))";
+    const result = await connection.runAndReadAll(querySql);
+
+    if (result.currentRowCount) {
+      return NextResponse.json(
+        { udis: result.getRowObjectsJson() },
+        { status: 200 },
+      );
+    } else {
+      return NextResponse.json(
+        { message: "Aucune UDI ne correspond à ces coordonnées" },
+        { status: 400 },
+      );
+    }
path: webapp/app/api/udi/find/route.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Code d'erreur inapproprié pour "aucun résultat trouvé"**

Lorsqu'aucune UDI ne correspond aux coordonnées, vous retournez un code 400 (Bad Request). Un code 404 (Not Found) serait plus approprié sémantiquement, puisque la requête est valide mais aucune ressource ne correspond.


```diff
if (result.currentRowCount) {
  return NextResponse.json(
    { udis: result.getRowObjectsJson() },
    { status: 200 },
  );
} else {
  return NextResponse.json(
    { message: "Aucune UDI ne correspond à ces coordonnées" },
-    { status: 400 },
+    { status: 404 },
  );
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    if (result.currentRowCount) {
      return NextResponse.json(
        { udis: result.getRowObjectsJson() },
        { status: 200 },
      );
    } else {
      return NextResponse.json(
        { message: "Aucune UDI ne correspond à ces coordonnées" },
        { status: 404 },
      );
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 5796752
created_at: 2025-04-01T21:34:15Z
author_association: NONE
diff_hunk: @@ -0,0 +1,68 @@
+// an api route fetching data
+
+import db from "@/app/lib/duckdb";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest) {
+  const { searchParams } = new URL(req.url);
+  const lonParam = searchParams.get("lon");
+  const latParam = searchParams.get("lat");
+
+  if (lonParam == null || latParam == null) {
+    return NextResponse.json(
+      { message: "Paramètres manquants: lon et lat sont requis" },
+      { status: 400 },
+    );
+  }
+  const lon = parseFloat(lonParam);
+  const lat = parseFloat(latParam);
+
+  if (
+    isNaN(lon) ||
+    isNaN(lat) ||
+    lon < -180 ||
+    lon > 180 ||
+    lat < -90 ||
+    lat > 90
+  ) {
+    return NextResponse.json(
+      { message: "Paramètres invalides" },
+      { status: 400 },
+    );
+  }
+
+  const connection = await db.connect();
+  try {
+    await connection.run("LOAD spatial;");
+    const querySql =
+      "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
+      lon +
+      " " +
+      lat +
+      ")' ))";
+    const result = await connection.runAndReadAll(querySql);
path: webapp/app/api/udi/find/route.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Risque d'injection SQL dans la construction de la requête**

Attention, la construction de la requête SQL utilise la concaténation directe des paramètres utilisateur, ce qui présente un risque d'injection SQL. Même si les valeurs sont validées comme des nombres, il est toujours recommandé d'utiliser des paramètres préparés.


```diff
const connection = await db.connect();
try {
  await connection.run("LOAD spatial;");
-  const querySql =
-    "SELECT * EXCLUDE(geom) FROM atlasante_udi WHERE ST_Contains(geom, ST_GeomFromText( 'POINT(" +
-    lon +
-    " " +
-    lat +
-    ")' ))";
-  const result = await connection.runAndReadAll(querySql);
+  // Utiliser des paramètres nommés au lieu de la concaténation directe
+  const pointWKT = `POINT(${lon} ${lat})`;
+  const querySql = `
+    SELECT * EXCLUDE(geom) 
+    FROM atlasante_udi 
+    WHERE ST_Contains(geom, ST_GeomFromText(?))
+  `;
+  const result = await connection.runAndReadAll(querySql, [pointWKT]);
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  const connection = await db.connect();
  try {
    await connection.run("LOAD spatial;");
    // Utiliser des paramètres nommés au lieu de la concaténation directe
    const pointWKT = `POINT(${lon} ${lat})`;
    const querySql = `
      SELECT * EXCLUDE(geom) 
      FROM atlasante_udi 
      WHERE ST_Contains(geom, ST_GeomFromText(?))
    `;
    const result = await connection.runAndReadAll(querySql, [pointWKT]);
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 5796752
created_at: 2025-04-01T21:34:15Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-01T20:06:51Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Review was skipped due to path filters
> 
> <details>
> <summary>:no_entry: Files ignored due to path filters (1)</summary>
> 
> * `pipelines/tasks/trim_database_for_website.py` is excluded by none and included by none
> 
> </details>
> 
> CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including `**/dist/**` will override the default block on the `dist` directory, by removing the pattern from both the lists.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
Ce pull request introduit une nouvelle route API GET (`webapp/app/api/udi/find/route.tsx`) qui récupère des données géospatiales en fonction des paramètres `lon` et `lat`. La route effectue des validations sur ces paramètres, exécute une requête SQL sur la table `atlasante_udi` via une connexion à une base DuckDB, et gère divers scénarios d’erreurs. De plus, le fichier `webapp/app/lib/duckdb.ts` est mis à jour pour installer et charger l’extension spatiale, améliorant ainsi les capacités géospatiales de la base.

## Changes

| Fichier(s)                                | Résumé des modifications                                                                                                                                                                                                                   |
|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `webapp/app/api/udi/find/route.tsx`         | Ajout d’une route `GET` pour extraire et valider les paramètres `lon` et `lat`. Exécution d’une requête SQL sur la table `atlasante_udi`, avec gestion d’erreur et fermeture de la connexion. |
| `webapp/app/lib/duckdb.ts`                  | Ajout d’instructions pour installer et charger l’extension spatiale sur la connexion DuckDB, permettant l’utilisation de fonctionnalités géospatiales.                                                                                   |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant Client as Client
    participant Route as GET /udi/find
    participant DuckDB as DuckDB
    
    Client->>Route: Envoie requête GET avec lon & lat
    Route->>Route: Valide les paramètres
    alt Paramètres invalides
        Route-->>Client: Retourne 400 (erreur de validation)
    else Paramètres valides
        Route->>DuckDB: Établit connexion et charge extension spatiale
        Route->>DuckDB: Exécute la requête SQL
        alt Résultats trouvés
            DuckDB-->>Route: Renvoie résultats
            Route-->>Client: Retourne 200 avec données
        else Aucun résultat
            DuckDB-->>Route: Aucun enregistrement
            Route-->>Client: Retourne 404 (aucun UDI trouvé)
        end
    end
```

## Suggested labels
`team-webdev`

## Suggested reviewers
- sandragjacinto

## Poem
> Dans le code, une nouvelle route s'éveille,  
> Où les données géospatiales en douceur se réveillent,  
> Des coordonnées dansent sur un air précis,  
> Tandis que DuckDB charge son extension avec brio et délice,  
> Nos lignes de code chantent en harmonie,  
> Pour Data For Good, un chemin innovant et ravivant.  
> 🎉🚀

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=dataforgoodfr/13_pollution_eau&utm_content=135):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: jereze
created_at: 2025-04-01T21:59:51Z
body: Merci @sbsrouteur ! :) 
author_association: COLLABORATOR