From a3e316ae130575a961287fcf31026c0df4cd01ae Mon Sep 17 00:00:00 2001
From: Hugo NOUTS <hnouts@grandlyon.com>
Date: Tue, 28 Feb 2023 16:01:06 +0000
Subject: [PATCH] fix(structureService): setCNFSid now checks if the cnfsID has
 meeting opened on rdvs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Remove an existing cnfsID when not matching espace coop or rdvs meeting list. Call rdvs api when a match is found on espace coop to avoid too many request.

⚠️ Warning ⚠️ : rdvs api might throw a 429 error when above 50 requests.
---
 .../services/structures.service.spec.ts       | 10 ++--
 src/structures/services/structures.service.ts | 56 +++++++++++++++----
 2 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/src/structures/services/structures.service.spec.ts b/src/structures/services/structures.service.spec.ts
index 45e18e3a8..7d3573277 100644
--- a/src/structures/services/structures.service.spec.ts
+++ b/src/structures/services/structures.service.spec.ts
@@ -595,7 +595,9 @@ describe('StructuresService', () => {
       expect((await service.getCNFSStructures()).length).toBe(2);
     });
     it('bindOneCNFSStructure(): should update ONE structure', async () => {
+      const mockSetCNFSid = jest.fn().mockResolvedValue(mockResinStructures[0]);
       jest.spyOn(service, 'findOne').mockResolvedValue(mockResinStructures[0] as StructureDocument);
+      jest.spyOn(service, 'setCNFSid').mockImplementation(mockSetCNFSid);
       mockStructureModel.findByIdAndUpdate.mockReturnThis();
       mockStructureModel.exec.mockResolvedValueOnce(mockResinStructures[0]);
       expect(await service.bindOneCNFSStructure('mockId')).toBe('A match has been found, updating CNFSid');
@@ -606,13 +608,11 @@ describe('StructuresService', () => {
       expect(await service.bindOneCNFSStructure('mockId')).toBe('No match found');
     });
     it('bindCNFSids(): should update 2 structures (matching phone and mail)', async () => {
+      const mockSetCNFSid = jest.fn().mockResolvedValue(mockResinStructures[0]);
       jest.spyOn(service, 'findAll').mockResolvedValue(mockResinStructures as StructureDocument[]);
-      mockStructureModel.findByIdAndUpdate.mockReturnThis();
-      mockStructureModel.findById.mockReturnThis();
-      mockStructureModel.limit.mockReturnThis();
-      mockStructureModel.populate.mockReturnThis();
-      mockStructureModel.exec.mockResolvedValueOnce([]);
+      jest.spyOn(service, 'setCNFSid').mockImplementation(mockSetCNFSid);
       expect(await service.bindCNFSids()).toBe(`2 structures affected`);
+      expect(mockSetCNFSid).toHaveBeenCalledTimes(2);
     });
   });
 });
diff --git a/src/structures/services/structures.service.ts b/src/structures/services/structures.service.ts
index 1e96d566c..eb33399cc 100644
--- a/src/structures/services/structures.service.ts
+++ b/src/structures/services/structures.service.ts
@@ -1059,9 +1059,15 @@ export class StructuresService {
 
     const matchingStructure = this.findMatchingStructure(resinStructure, CNFSData);
     if (matchingStructure) {
-      // if a corresponding structure has been found but its email is different, we probably will need to update it here.
-      await this.setCNFSid(resinStructure.id, matchingStructure.structureId);
-      return 'A match has been found, updating CNFSid';
+      try {
+        const result = await this.setCNFSid(resinStructure.id, matchingStructure.structureId);
+        if (result) {
+          return 'A match has been found, updating CNFSid';
+        }
+      } catch (error) {
+        this.logger.error(`Error updating CNFSid: ${error.message}`);
+        return 'A match has been found, but there was an error updating CNFSid';
+      }
     }
     this.logger.debug(`No match found`);
     return 'No match found';
@@ -1084,28 +1090,58 @@ export class StructuresService {
     for (const resinStructure of resinStructures) {
       const matchingStructure = this.findMatchingStructure(resinStructure, CNFSData);
       if (matchingStructure) {
-        if (await this.setCNFSid(resinStructure.id, matchingStructure.structureId)) {
-          matchCount++;
+        try {
+          const result = await this.setCNFSid(resinStructure.id, matchingStructure.structureId);
+          if (result) {
+            matchCount++;
+          }
+        } catch (error) {
+          this.logger.error(`Error while setting CNFS ID for structure ${resinStructure.id}: ${error.message}`);
         }
         // if a corresponding structure has been found but its email is different, we probably will need to update it here.
       } else {
-        if (await this.setCNFSid(resinStructure.id, '')) {
-          mismatchCount++;
+        mismatchCount++;
+        if (resinStructure.idCNFS) {
+          // to reset the cnfs id field if it was previously set
+          this.logger.warn(`Cleaning old CNFS id '${resinStructure.idCNFS}'`);
+          const setCnfsToNull = Object.assign(resinStructure, { idCNFS: '' });
+          this.structureModel.findByIdAndUpdate(new Types.ObjectId(resinStructure.id), setCnfsToNull).exec();
         }
       }
     }
     this.logger.log(
-      `${matchCount} structures affected with a idCNFS (${mismatchCount} structures with idCNFS emptied)`
+      `${matchCount} structures affected with an idCNFS (${mismatchCount} structures not matching espace coop ids)`
     );
     return `${matchCount} structures affected`;
   }
 
   /**
-   * Update the idCNFS of a structure with its value of ""
+   * Update the idCNFS of a structure if it has meetings open on rdvs"
    */
   public async setCNFSid(idStructure: string, idCNFS: string): Promise<Structure> {
     const oldStructure = await this.findOne(idStructure);
-    if (oldStructure.idCNFS || '' == idCNFS) {
+    // Call the rdvs API to check if the idCNFS is open for meetings
+    try {
+      const apiResponse = await lastValueFrom(
+        this.httpService.get(`https://www.rdv-aide-numerique.fr/public_api/public_links?territory=CN`)
+      );
+      const matchingExternalIds = apiResponse.data.public_links.filter((item) => item.external_id === idCNFS);
+
+      if (matchingExternalIds.length === 0) {
+        this.logger.warn(`CNFS id '${idCNFS}' is not open for meetings`);
+        if (oldStructure.idCNFS) {
+          this.logger.warn(`Cleaning old CNFS id '${oldStructure.idCNFS}'`);
+          const setCnfsToNull = Object.assign(oldStructure, { idCNFS: '' });
+          return this.structureModel.findByIdAndUpdate(new Types.ObjectId(idStructure), setCnfsToNull).exec();
+        }
+        return null;
+      }
+    } catch (error) {
+      this.logger.warn(`Error while checking CNFS id '${idCNFS}' for meetings: ${error.message}`);
+      return null;
+    }
+    // no need for an update if the cnfs id match and is already known (in order to not mess with the structure activity recording when it was lastly updated for mail inquiries)
+    if (oldStructure.idCNFS === idCNFS) {
       return null;
     }
     this.logger.log(`A resin structure '${idStructure}' matched with a CNFS one, updating its idCNFS '${idCNFS}'`);
-- 
GitLab