From 66054863ffda381a5f15d9419e3577717b2c9798 Mon Sep 17 00:00:00 2001
From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com>
Date: Fri, 18 Dec 2020 17:17:26 +0100
Subject: [PATCH] feat: add APTIC api structure + cron job

---
 package-lock.json                             | 37 +++++++++
 package.json                                  |  1 +
 src/app.module.ts                             |  2 +
 .../services/aptic-structures.service.ts      | 78 +++++++++++++++++++
 .../{ => services}/structures.service.ts      |  4 +-
 src/structures/structures.controller.ts       |  2 +-
 src/structures/structures.module.ts           |  5 +-
 template.env                                  |  1 +
 8 files changed, 125 insertions(+), 5 deletions(-)
 create mode 100644 src/structures/services/aptic-structures.service.ts
 rename src/structures/{ => services}/structures.service.ts (96%)

diff --git a/package-lock.json b/package-lock.json
index f17f7e9a4..6e30b44fd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1394,6 +1394,22 @@
         "tslib": "2.0.3"
       }
     },
+    "@nestjs/schedule": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-0.4.1.tgz",
+      "integrity": "sha512-pj+zo3DJnoyGQKGguyLn9Nv1KEHZO2vNNGhtrZCIn74GsJL+CkDnd+fpgV85mypaJzjjGRogbMvXUW2UFnJAfg==",
+      "requires": {
+        "cron": "1.7.2",
+        "uuid": "8.3.0"
+      },
+      "dependencies": {
+        "uuid": {
+          "version": "8.3.0",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
+          "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
+        }
+      }
+    },
     "@nestjs/schematics": {
       "version": "7.1.3",
       "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-7.1.3.tgz",
@@ -4482,6 +4498,14 @@
         "sha.js": "^2.4.8"
       }
     },
+    "cron": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/cron/-/cron-1.7.2.tgz",
+      "integrity": "sha512-+SaJ2OfeRvfQqwXQ2kgr0Y5pzBR/lijf5OpnnaruwWnmI799JfWr2jN2ItOV9s3A/+TFOt6mxvKzQq5F0Jp6VQ==",
+      "requires": {
+        "moment-timezone": "^0.5.x"
+      }
+    },
     "cross-spawn": {
       "version": "6.0.5",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -9796,6 +9820,19 @@
       "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz",
       "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="
     },
+    "moment": {
+      "version": "2.29.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
+      "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+    },
+    "moment-timezone": {
+      "version": "0.5.32",
+      "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz",
+      "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==",
+      "requires": {
+        "moment": ">= 2.9.0"
+      }
+    },
     "mongodb": {
       "version": "3.6.3",
       "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz",
diff --git a/package.json b/package.json
index 939149dd3..be649e40e 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
     "@nestjs/mongoose": "^7.1.0",
     "@nestjs/passport": "^7.1.5",
     "@nestjs/platform-express": "^7.5.1",
+    "@nestjs/schedule": "^0.4.1",
     "@nestjs/swagger": "^4.7.5",
     "@types/bcrypt": "^3.0.0",
     "bcrypt": "^5.0.0",
diff --git a/src/app.module.ts b/src/app.module.ts
index 33d12ac3d..0fdca258b 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -1,5 +1,6 @@
 import { Module } from '@nestjs/common';
 import { MongooseModule } from '@nestjs/mongoose';
+import { ScheduleModule } from '@nestjs/schedule';
 import { AppController } from './app.controller';
 import { StructuresModule } from './structures/structures.module';
 import { ConfigurationModule } from './configuration/configuration.module';
@@ -13,6 +14,7 @@ import { MailerModule } from './mailer/mailer.module';
     MongooseModule.forRoot(
       `mongodb://${process.env.MONGO_NON_ROOT_USERNAME}:${process.env.MONGO_NON_ROOT_PASSWORD}@${process.env.MONGO_DB_HOST_AND_PORT}/ram`
     ),
+    ScheduleModule.forRoot(),
     StructuresModule,
     CategoriesModule,
     AuthModule,
diff --git a/src/structures/services/aptic-structures.service.ts b/src/structures/services/aptic-structures.service.ts
new file mode 100644
index 000000000..838a221e8
--- /dev/null
+++ b/src/structures/services/aptic-structures.service.ts
@@ -0,0 +1,78 @@
+import { HttpException, HttpService, Injectable, HttpStatus } from '@nestjs/common';
+import { Logger } from '@nestjs/common';
+import { Observable } from 'rxjs';
+import { AxiosResponse } from 'axios';
+import { Cron, CronExpression } from '@nestjs/schedule';
+import * as _ from 'lodash';
+import * as https from 'https';
+
+@Injectable()
+export class ApticStructuresService {
+  constructor(private readonly httpService: HttpService) {}
+
+  public formatApticStructures(postalCodeData: any[]): any {
+    // Get all postal code in one array
+    const postalCodeArray = _.flatten(
+      postalCodeData.map((data) => {
+        return data.codesPostaux;
+      })
+    );
+
+    // Call APTIC Api's
+    const postalCodePromises = postalCodeArray.map((postalCode) => {
+      return this.getApticStructures(postalCode).toPromise();
+    });
+
+    Promise.all(postalCodePromises).then((data) => {
+      const structuresData = _.flatten(
+        data.map((tmp: { data }) => {
+          return tmp.data.data;
+        })
+      );
+      console.log(structuresData);
+      //TODO: create structure
+      //TODO: clean with already existing structures
+    });
+  }
+
+  @Cron(CronExpression.EVERY_MINUTE)
+  public async getMetopoleMunicipality() {
+    const req =
+      'https://download.data.grandlyon.com/ws/grandlyon/adr_voie_lieu.adrcomgl/all.json?maxfeatures=-1&start=1';
+    Logger.log(`Request : ${req}`, 'ApticStructuresService - getMetopoleMunicipality');
+    this.httpService.get(encodeURI(req)).subscribe(
+      (data) => {
+        const inseeArray = data.data.values.map((municipality) => {
+          return this.getPostalCodeWithINSEE(municipality.insee).toPromise();
+        });
+        Promise.all(inseeArray).then((inseData) => {
+          const postalCodeArray = inseData.map((cpData: { data; config }) => {
+            return cpData.data;
+          });
+          this.formatApticStructures(postalCodeArray);
+        });
+      },
+      (err) => Logger.error(err)
+    );
+  }
+
+  public getPostalCodeWithINSEE(inseeCode: string): Observable<AxiosResponse<any>> {
+    const req = `https://geo.api.gouv.fr/communes/${inseeCode}?fields=codesPostaux&format=json`;
+    Logger.log(`Request : ${req}`, 'ApticStructuresService - getMetopoleMunicipality');
+    return this.httpService.get(encodeURI(req));
+  }
+
+  public getApticStructures(postalCodeData: string): Observable<AxiosResponse<any>> {
+    const agent = new https.Agent({
+      rejectUnauthorized: false,
+    });
+    const req = `https://presence.aptic.fr/postal_code/${postalCodeData}`;
+    Logger.log(`Request : ${req}`, 'ApticStructuresService');
+    return this.httpService.get(req, {
+      httpsAgent: agent,
+      headers: {
+        api_key: process.env.APTIC_TOKEN,
+      },
+    });
+  }
+}
diff --git a/src/structures/structures.service.ts b/src/structures/services/structures.service.ts
similarity index 96%
rename from src/structures/structures.service.ts
rename to src/structures/services/structures.service.ts
index 543058c8c..785c6d1aa 100644
--- a/src/structures/structures.service.ts
+++ b/src/structures/services/structures.service.ts
@@ -3,8 +3,8 @@ import { InjectModel } from '@nestjs/mongoose';
 import { Model } from 'mongoose';
 import { Observable } from 'rxjs';
 import { AxiosResponse } from 'axios';
-import { CreateStructureDto } from './dto/create-structure.dto';
-import { Structure, StructureDocument } from './schemas/structure.schema';
+import { CreateStructureDto } from '../dto/create-structure.dto';
+import { Structure, StructureDocument } from '../schemas/structure.schema';
 import { Logger } from '@nestjs/common';
 
 @Injectable()
diff --git a/src/structures/structures.controller.ts b/src/structures/structures.controller.ts
index 8026e1747..cb5cd41e7 100644
--- a/src/structures/structures.controller.ts
+++ b/src/structures/structures.controller.ts
@@ -2,7 +2,7 @@ import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';
 import { CreateStructureDto } from './dto/create-structure.dto';
 import { QueryStructure } from './dto/query-structure.dto';
 import { Structure } from './schemas/structure.schema';
-import { StructuresService } from './structures.service';
+import { StructuresService } from './services/structures.service';
 
 @Controller('structures')
 export class StructuresController {
diff --git a/src/structures/structures.module.ts b/src/structures/structures.module.ts
index 6a82aae92..bd2d9cda6 100644
--- a/src/structures/structures.module.ts
+++ b/src/structures/structures.module.ts
@@ -3,11 +3,12 @@ import { MongooseModule } from '@nestjs/mongoose';
 import { MailerModule } from '../mailer/mailer.module';
 import { Structure, StructureSchema } from './schemas/structure.schema';
 import { StructuresController } from './structures.controller';
-import { StructuresService } from './structures.service';
+import { StructuresService } from './services/structures.service';
+import { ApticStructuresService } from './services/aptic-structures.service';
 
 @Module({
   imports: [MongooseModule.forFeature([{ name: Structure.name, schema: StructureSchema }]), HttpModule, MailerModule],
   controllers: [StructuresController],
-  providers: [StructuresService],
+  providers: [StructuresService, ApticStructuresService],
 })
 export class StructuresModule {}
diff --git a/template.env b/template.env
index 79db55d3f..4f50e814d 100644
--- a/template.env
+++ b/template.env
@@ -13,3 +13,4 @@ ME_PORT=<mongo express port>
 SALT=<Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue>
 MAIL_URL=<API url>
 MAIL_TOKEN=<API token>
+APTIC_TOKEN=<APTIC API TOKEN>
-- 
GitLab